Lecture 5

Object-Oriented Programming

Announcements

  • Grades for assignment 1 will be out by Friday
  • Assignment two is out now. It's due March 2
  • That is all.

(OOP)

Remember how we talked about how tuples, dictionaries, and classes share some similarities?
And how they can all be used define heterogeneous types?

But I also said:

"Think of classes as tuples/dictionaries+++"

—me

Notice how our class has methods that belong to it.

These methods operate on the data that the instance contains.


        class Transfer:

            def __init__(self, sender, receiver, amount, date=datetime.now()):
                self.sender = sender
                self.receiver = receiver
                self.amount = amount
                self.date = date
                self.processing_fee_applied = False

            def days_ago(self):
                time_delta = datetime.now() - self.date
                return time_delta.days

            def apply_processing_fee(fee=Decimal("0.25")):
                self.amount = self.amount + fee
                self.processing_fee_applied = True
    

This is called encapsulation.

  • Lets us bundle data with methods that operate on it
  • Methods provide an interface to simplify computations on object data:
    
                    def days_ago(self):
                        time_delta = datetime.now() - self.date
                        return time_delta.days
                
  • Methods hide complicated, internal implementation details:
    
                    def apply_processing_fee(fee=Decimal("0.25")):
                        self.amount = self.amount + fee
                        self.processing_fee_applied = True
            

In a nutshell, encapsulation means binding data and behaviors together in a single unit: the class.

In Python (unlike some languages) class variables can never be 100% encapsulated.


        # Given:
        t1 = Transfer("Bob", "Sally", Decimal(35.50), datetime(2019, 10, 1))

        # Valid:
        t1.amount = Decimal("10000000000.02")
        # Valid:
        del t1.sender

        # In fact...
        t1.any_property_you_feel_like = 12345
    

Inheritance

Let's say you're opening a pet store. The pet store of the future, where everything is run by computer...

RoboPet

We will sell cats, so our system must track them and care for them. What are some attributes of cats?

  • Age
  • Weight
  • Name
  • Breed

How do we care for cats?

  • Feed them
  • Groom them
  • Change their litter

        class Cat:

            def __init__(self, age, weight, name, breed):
                self.age = age
                self.weight = weight
                self.name = name
                self.breed = breed

            def feed(self, scoops=1):
                ...

            def groom(self):
                ...

            def change_litter(self):
                ...
    

We will also sell dogs, so our system must track them and care for them too. What are some dog attributes?

  • Age
  • Weight
  • Name
  • Breed
  • Is housebroken?

How do we care for dogs?

  • Feed them
  • Groom them
  • Walk them

        class Dog:

            def __init__(self, age, weight, name, breed, housebroken=True):
                self.age = age
                self.weight = weight
                self.name = name
                self.breed = breed
                self.housebroken = housebroken

            def feed(self, scoops=1):
                ...

            def groom(self):
                ...

            def walk(self):
                ...
    
Attributes
  • Age
  • Weight
  • Name
  • Breed
  • Age
  • Weight
  • Name
  • Breed
  • Is housebroken?
Methods
  • Feed
  • Groom
  • Change litter
  • Feed
  • Groom
  • Walk

D.R.Y.


class Animal:

    def __init__(self, age, weight, name, breed):
        self.age = age
        self.weight = weight
        self.name = name
        self.breed = breed

    def feed(self, scoops=1):
        # Pour food into bowl.

    def groom(self):
        # Brushy-brushy time
    

Subclasses automatically have the methods of the superclass.


class Cat(Animal):

    def change_litter(self):
        # Yuck!
    

class Dog(Animal):

    def __init__(self, age, weight, name, breed, housebroken=True):
        super().__init__(age, weight, name, breed)
        self.housebroken = housebroken

    def walk(self):
        # Walk the dog around the neighborhood.
    

In Python, the super function will return the parent class.


class Child(Parent):

    def some_method(self):
        # Super gives us access to the Parent class.
        parent_obj = super()

    

Interfaces


class Animal:

    def groom(self):
        raise NotImplementedError("Must implement this method on the child object.")

class Cat(Animal):

    def groom(self):
        # Bushy brushy...

class Dog(Animal):

    def groom(self):
        # Bath time!
        # Dry up!

    

Inheritance lets us abstract common behavior while still being able to encapsulate attributes and methods within a class.

  • Implement shared functionality only once.
  • Maps to how we think about the world: "X is a Y"

Polymorphism

Sounds fancy, but it's not.

  • Encapsulation & inehritance provide the building blocks
  • Different behaviors based on subclass being used...
  • ...and without having to care what specific subclass.

Think back to interfaces:


class Animal:

    def __init__(self, age, weight, name, breed):
        self.age = age
        self.weight = weight
        self.name = name
        self.breed = breed

    def feed(self, scoops=1):
        # Pour food into bowl.

    def groom(self):
        raise NotImplementedError("Must implement this method on the child object.")
        

Interfaces provide a sort of contract: These are the properties and behaviors any animal must have.

RoboPet

The robots were a success. But we can't scale up the business if we need a robot for each pet.

We come up with a new concept: a virtual pen

RoboPet

  • Each pen can hold up to 4 pets.
  • Our animals are very well-behaved, so cats and dogs can live in the same pen.
  • Each pen will be managed by one robot.
  • We only need ¼ as many robots now.

RoboPet


class Pen:

        def __init__(???):
            """ What do we do now?"""
    

Let's code...

Polymorphisnm IRL

The file object is a real-life example of polymorphism.

  • I want to open a file, and use the object returned to go through a file line-by-line.
  • The file might be an an actual file on disk.
  • Or it might be stored-in memory.
  • Or it might be data I'm reading from a network socket.
  • Or it might be URL that I fetch from the web.

Before OOP, the main question was “What should the program do and in what order?”

This is called procedural programming.

  1. Split your program up into a set of tasks and subtasks
  2. Write functions that perform the tasks.
  3. Instruct the computer to perform the tasks in sequence

Then someone asked: "What if we started by thinking about programming in terms of the objects that model our domain instead?"

Alan Kay
  1. Start by thinking about the classes that describe your domain
  2. Think about the properties each class needs
  3. Think about the behaviors each class should have

How to make objects useful in a world like this?

  • Classes are blueprints for objects
  • Encapsulation
  • Inheritance
  • Polymorphism

OOP & its discontents

Java is the most distressing thing to happen to computing since MS-DOS.
—Alan Kay

The tyranny of "King Noun".

Sometimes, the elegant implementation is just a function.
Not a method. Not a class. Not a framework. Just a function.

John Carmack

State gets atomized among a million objects, making it hard to understand holistically.

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
Joe Armstrong

Inheritance hierarchies don't map
to real-world all that well.

Object-oriented programming is an exceptionally bad idea
which could only have originated in California

Edsger W. Dijkstra

One of the nice things about Python is that it's not dogmatic.

Practicality beats purity; use OOP where it helps and fall back to simpler, procedural code when it makes sense.