Beginner · Lesson 34 of 94

Object-oriented programming: the big picture

Watch this lesson

View full playlist ↗

Read the lesson

What you'll learn

Concept

You have been using objects since the first lesson. A string is an object: it holds data, the characters, and it carries behavior, methods like upper() and split(). A list is an object with append and sort. When you write name.upper(), you are asking the object to do something with its own data. Object-oriented programming is nothing more exotic than building your own kinds of objects, so that your data and the operations on it live together the way strings and lists already do.

Object-oriented programming: the big picture: object: 'hello' → data: the characters → behavior: upper(), split(), find() → object: [3, 1, 2 → data: the items → behavior: append(), sort(), pop()
Object-oriented programming: the big picture · Figure 1 · Open for full size

A class is the blueprint; an object is one thing built from it. str is a class, and "hello" is an object of that class, also called an instance. type(x) tells you the class of any object. When you write your own class, class BankAccount:, you are defining a new type, and every account you then create is an instance with its own balance and the same set of operations. Two accounts share the class and differ in their data.

Why bother, when functions and dictionaries already work? Because a dictionary of account data plus a pile of functions that take dictionaries has no boundary: any code can reach in and set the balance to a string, and the functions that belong to accounts are scattered among everything else. A class draws the boundary. The data is inside, the operations are attached, and a reader can see in one place what an account is and what it can do.

Without objects With objects
account = {"owner": "ada", "balance": 100} account = BankAccount("ada", 100)
deposit(account, 50) account.deposit(50)
anyone can write account["balance"] = "oops" the class decides how balance changes
functions for accounts live anywhere they live in the class, next to the data

The four pillars are the vocabulary you will hear in every interview and read in every codebase. Encapsulation: data and the operations on it live together, and the object controls how its data changes. Abstraction: you use an object through a small set of methods without knowing how it works inside, the way you use list.sort() without knowing the algorithm. Inheritance: a class can be built on another class, gaining its behavior and adding or changing some. Polymorphism: different classes can offer the same method, so code that calls .area() works on a circle and a square alike.

Object-oriented programming: the big picture: Encapsulation; data + behavior together, controlled access → Abstraction; use it through a few methods → Inheritance; build a class on another → Polymorphism; same method name, different classes
Object-oriented programming: the big picture · Figure 2 · Open for full size

Everything in Python is an object, including functions, modules, and classes themselves. That is why help(len) works, why you can pass a function to sorted as a key, and why import math gives you something with attributes. You do not need to hold all of that in your head yet. You need only the mental model: an object is data plus behavior, a class describes a kind of object, and the next lessons teach you to write them.

Code

Objects you already know, inspected. Every value has a class, and every class defines the methods its objects can perform.

# already_objects.py
name = "hello"
items = [3, 1, 2]
number = 42

for value in (name, items, number, len, print):
    print(f"{value!r:<26} is an instance of {type(value).__name__}")

print("upper" in dir(name), "append" in dir(items), "append" in dir(name))
Output
'hello'                    is an instance of str
[3, 1, 2]                  is an instance of list
42                         is an instance of int
<built-in function len>    is an instance of builtin_function_or_method
<built-in function print>  is an instance of builtin_function_or_method
True True False

The same idea two ways: a dictionary plus a function, then a class. Read the class as a preview of the next lesson, and notice how the operation and the data now travel together.

# two_ways.py
def deposit(account, amount):
    account["balance"] += amount


class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount


dict_account = {"owner": "ada", "balance": 100}
deposit(dict_account, 50)
print(dict_account)

obj_account = BankAccount("ada", 100)
obj_account.deposit(50)
print(obj_account.owner, obj_account.balance)
print(type(obj_account).__name__, isinstance(obj_account, BankAccount))
Output
{'owner': 'ada', 'balance': 150}
ada 150
BankAccount True

Polymorphism in the built-ins, before you write any class. One loop calls len() and iterates over three different kinds of object, because all three support the same operations.

# polymorphic_builtin.py
things = ["abc", [1, 2, 3, 4], {"x": 1, "y": 2}]

for thing in things:
    print(type(thing).__name__, len(thing), [item for item in thing])
Output
str 3 ['a', 'b', 'c']
list 4 [1, 2, 3, 4]
dict 2 ['x', 'y']

Try it yourself

Create inspect_objects.py. Make one value of each type you know (str, int, float, bool, list, tuple, dict, set) and, for each, print its class name and three of its method names from dir() that do not start with an underscore. Then write one sentence per value describing its data and one behavior, in a comment. You are describing objects; the next lesson shows you how to define them.

Common mistakes

What's next

You have the picture. Next you will write your first class, create instances from it, and give them their own state.

Course overview