Object Oriented Programming Quick Review

Object = Something with unique characteristics and functions

what is the point of object oriented programming?

Sometimes, when coding, we want to create many of the same type of object. Rather than defining the same type of thing over and over again, we can create what is called a class, which is sort of like a template for a particular object, and every time we want to create a new one of these objects, we can create what is called an instance of a class. This prevents us from having to write a lot of redundant code. Every class contains an init method, which is what is called whenever we create a new instance of a particular class.

class Car:
    num_wheels = 4
    def __init__(self, color, make):
        self.color = color
        self.make = make
    def drive(self):
        return 'vroom vroom'
    def park(self):
        if self.num_wheels == 4:
            return 'In between the white lines!'
        else:
            return 'Oof, you better find a new spot :('
    def paint(self):
        return 'Added new ' + self.color + ' paint'
jeep = Car('black', 'jeep')

some important definitions

Instance attribute = A variable that is specific to a particular instance of a class

  • In the car class, color and make are examples of instance attributes because they are not the same for all cars

Class attribute = A variable that is a part of the class, and is thus shared across all instances of a class

  • num_wheels is a class attribute because it is defined outside of the init method, and thus shared across all instances

Whenever we try to find the value of a particular attribute (such as jeep.color), we always look in the instance first (at instance attributes), then at the class (class attributes), then to any parent classes (if there is any inheritance).

Methods = Functions within a class

  • init, drive, park, and paint are methods of the Car class

dot notation

When we access methods and variables in object oriented programming, we use what’s called dot notation. Since we are now working with methods, not regular functions, we can no longer just say things like drive() and park(). To access any method inside of a class, you can always do class_name.method(parameters).

Now you may be wondering, what is this self thing? Well, as a python convention, in a class we generally use self to refer to an instance of that particular class. That way, when we write self.some_attribute, we can get access to instance or class attributes associated with a particular instance. However, it is important to note that self is not a special keyword in python, and technically you could pass something that is not an instance of the current class in as self, but depending on the body of the method, you may run into some errors if self doesn’t have all of the attributes that are requested in the method.

Since self is the variable we use to represent an instance of the class that a method is a part of, instead of doing class_name.method(parameters), we can also do instance_name.method(any params other than self), which will call the method inside instance_name’s class with instance_name as the self parameter. We can do this because an instance of a class has access to all of the methods of its class.

For example, if we wanted to access jeep’s color, we would say:

jeep.color

And if we wanted to call the drive method with jeep as self, we could say:

jeep.drive()

So What Is This __INIT__ anyway?

It seems weird that every time we create an instance of a class, like when we defined jeep above, we just “called” the Car class, and it just knew to call the init method. Also what’s up with the double underscores?

Turns out that python has a bunch of “magic methods”, written as __name__, that we’ve actually been working with since the very beginning of 61A. For each of these magic methods, there are shortcut ways to implicitly call them without writing out the full name with double underscores and everything. One example that we’ve used as a lot is __eq__, which is implicitly called every time we use the == operator.

In the case of __init__, we can call this implicitly by following a class name with parentheses (and passing in any parameters as needed).

inheritance

Sometimes, we want to create an object that shares some attributes/methods with another existing object. Rather than creating a whole new object and copying a lot of code from our existing object, we can make our new object inherit from our existing object. All you have to do is put the existing object, or the parent class, in parentheses after the class name in the class definition.

For example, if we wanted to make a motorcycle class inherit from a car class we could do this:

class Motorcycle(Car):

Now, any instance of the Motorcycle class has access to all of the attributes and methods of the Car class.