Object-Oriented Programming
Shayna Kothari
Object-Oriented Programming
When going over data abstractions, we talked about representing different kinds of data using structures we already know.
However, instead of using just lists to represent things, we can create our own representations of data!
Fundamental idea: You can create your own types of data! Python ordinarily gives you some [e.g. lists], but you can create more to suit your needs.
Objects
What is an object?
An object is a type of data in Python. For example, lists are a type of object in Python.
Every object follows the structure of a class; each instantiation of an object is a specific instance.
Each instance has certain characteristics, called attributes, and can undertake certain actions using functions called methods.
Let’s talk about what this means! What are some characteristics students hold?
Attributes & Methods
Attributes are characteristics an object holds! These are divided into two types: class attributes and instance attributes.
Class attributes belong to all members of a class and are “shared” among them. Instance attributes belong only to a specific instance, and are instantiated when you create an instance or run a certain method of the instance.
One special kind of class attribute is the instance method (contrary to its name!). It’s a function that belongs to a class. Its name arises from the fact that it must take in an instance of the object as its first argument.
You can access any instance or class attributes using dot notation.
Student Class
class Student:
is_human = True
has_fun = True
def __init__(self, homework, classes):
self.classes = classes
self.homework = homework
def study(self, hours):
self.homework -= hours
if self.homework <= 0:
print('Done studying!')
Inheritance
Objects can follow the general structure of other objects, with some modifications!
Let’s say you wanted to write a BerkeleyStudent class. Berkeley students are still Students too, and do many of the same things as students generally! However, there might be some slight differences we want to take into account.
We can create new attributes in BerkeleyStudent; if we initialize our class correctly, it inherits everything from Student. Attributes defined in BerkeleyStudent overwrite attributes with the same names as things from Student .
What if we want to avoid rewriting logic defined in the superclass and just add to it? We can call the method we’re looking for by either doing something like Student.method(self, ____) or super().method(____)
BerkeleyStudent Class
class BerkeleyStudent(Student):
has_fun = False
def __init__(self, homework, classes):
Student.__init__(self, homework, classes)
self.homework *= 2
Attribute Order of Evaluation
When Python sees a request for an attribute [e.g. if you had a BerkeleyStudent named steph, and you were looking for steph.homework], it first looks for an instance attribute that corresponds to the attribute name; if it doesn't find an instance attribute, it then looks for a class attribute in the lowest-level class [BerkeleyStudent]. If it didn't find something in BerkeleyStudent, it would look at the parent class, Student, and so on until it hits Object.
vs.
Representation
__repr__
__str__
Some Important Interfaces
__repr__
__str__
__len__
__add__, __sub__, __mul__
__float__, __bool__, __int__
__getitem__(self, index)