Chapters
Python114 chapters

Classes and objectsChapter 59 of 114

Classes and Objects

Bundle data and the code that works on it into one thing.

A class is a template

A class describes a kind of thing. An object is one actual thing made from that template:

Python
class Dog:
    sound = "woof"

first = Dog()
second = Dog()

print(first.sound)
print(type(first).__name__)
print(first is second)

Output

woof
Dog
False

Dog() builds a new object each time. Two dogs, both with the same sound, but not the same object.

Why bundle at all

Without classes, related data drifts apart and every function needs all of it passed in:

Python
name = "Rex"
age = 3

def describe(name, age):
    return f"{name} is {age}"

print(describe(name, age))

Output

Rex is 3

That works for one dog. For fifty, you need parallel lists and the discipline to keep them lined up. A class keeps each dog's data together, and keeps the functions that work on it in the same place.

Attributes

Values attached to an object. You can set them from outside:

Python
class Dog:
    pass

rex = Dog()
rex.name = "Rex"
rex.age = 3
print(rex.name, rex.age)

Output

Rex 3

That works and it is a bad habit — nothing says which attributes a Dog is supposed to have, and a typo silently creates a new one. The __init__ chapter fixes this properly.

Class attributes versus instance attributes

An attribute on the class is shared by every object; one set on the object belongs to it alone:

Python
class Dog:
    species = "canis familiaris"

rex = Dog()
fido = Dog()
rex.name = "Rex"

print(rex.species, fido.species)
print(rex.name)
print(hasattr(fido, "name"))

Output

canis familiaris canis familiaris
Rex
False

Assigning through one object creates an instance attribute that shadows the class one, rather than changing it for everybody:

Python
class Dog:
    species = "canis"

rex = Dog()
fido = Dog()
rex.species = "wolf"

print(rex.species, fido.species, Dog.species)

Output

wolf canis canis
Python
class Dog:
    tricks = []

rex = Dog()
fido = Dog()
rex.tricks.append("sit")
print(fido.tricks)

Output

['sit']

There is one list, on the class. Per-object data belongs in __init__.

Objects are mutable

An object passed into a function is the same object, so changes show up everywhere:

Python
class Counter:
    pass

def bump(c):
    c.value += 1

counter = Counter()
counter.value = 0
bump(counter)
bump(counter)
print(counter.value)

Output

2

Everything is an object

Classes are not a separate world. Numbers, strings and functions are objects too, with types and attributes:

Python
print(type(5), type("a"), type([]))
print((5).bit_length())
print("abc".upper())

Output

<class 'int'> <class 'str'> <class 'list'>
3
ABC

Writing your own class is just adding a new type to the ones already there.

Test yourself

2 questions

What happens when two objects share a class attribute that is a list, and one appends to it?

Show the answer

Both see the change, because there is one list on the class — It is the same trap as a mutable default argument. Per-object data belongs in __init__.

What does assigning rex.species = "wolf" do to the class attribute?

Show the answer

Nothing; it creates an instance attribute that shadows it — Reading falls back to the class, but writing always lands on the object.

Next chapter

The __init__ Method

Set up each new object with the data it needs.