Classes and objects
A blueprint and the things made from it: your own type with its own data, and the methods that work on it.
A blueprint and the things made from it
A class is a blueprint for a kind of thing. It says what every one of those things holds and what it can do. An object is one thing made from the blueprint — one recipe, one card, one account.
The data an object carries are its attributes, and the functions that work on it are its methods. A class is how a program stops passing the same few values around together and gives them a home.
One blueprint, two recipes
class Recipe:
def __init__(self, name, minutes):
self.name = name
self.minutes = minutes
def describe(self):
return f"{self.name} takes {self.minutes} minutes."
flatbread = Recipe("Flatbread", 25)
soup = Recipe("Soup", 40)
print(flatbread.describe())
print(soup.describe())__init__ sets the object up
__init__ runs the moment an object is made, and its job is to put the starting values in place. Recipe("Flatbread", 25) calls it with two arguments, and the new object leaves it holding a name and a number of minutes.
The underscores on both sides mark a name Python treats specially — it looks for this one at creation time. You rarely write any others, but every class that carries data has this one.
self is this object
Inside a method, self is the object the method was called on. self.name = name means "store this value on this object" — and because every object has its own, the flatbread’s 25 minutes and the soup’s 40 never mix.
That is also why the call looks lopsided: flatbread.describe() takes no arguments, though def describe(self) asks for one. The object before the dot fills self; you just do not have to say it twice.
From dictionary to class
Turn the two values into a small Recipe class: an __init__ that stores the name and the minutes, and a describe() method that returns the sentence. Then make two recipes and print what each one says.
The dot looks the attribute up on the object
flatbread.describe() is two steps: find the attribute called describe on that object, then call it. The attribute comes from the class, because the object itself only stores data. That is why changing the class changes every object made from it, while changing one object’s minutes changes only that object.
A missing attribute raises AttributeError, and the message names the object’s type and the attribute it could not find. A typo in a method name and a method that was never defined look the same from the outside, so check the spelling before doubting the class.
Objects print in an unhelpful way by default
print(flatbread) shows something like <__main__.Recipe object at 0x...>, which says where the object lives and nothing about what it holds. That is not a bug; it is the default, and __str__ is the hook that replaces it. The address changes on every run, so it is useless for comparing two objects.
Until you write one, print the attributes you care about: print(flatbread.name, flatbread.minutes). The habit is useful anyway, because it forces you to decide which parts of an object are worth showing and which are internal. A describe method is often the better answer, and it can be reused by __str__ later.
If a few values always travel together, and the same functions always take all of them, they are asking to be a class.
Methods can change the object
A method is not limited to reading attributes. self.minutes += extra changes the object it was called on, and the new value is there for every later call. That is how an object keeps state across a program instead of being a bundle of values passed around.
Because each object has its own attributes, the change stays with that object. Two recipes made from the same class can hold different times, and extending one does not touch the other. The class describes the behaviour; each object carries its own copy of the data.
One method can call another
Inside one method, self can call another: self.summary() runs the other method on the same object. That is how a class builds a small vocabulary instead of repeating its own formatting in every method.
The double-underscore names such as __init__ are hooks Python looks for. __str__, for instance, decides what print(object) shows. You can ignore them until you need them; a single leading underscore is only a hint that a name is internal.
Three class mistakes: a method that forgets self in its parameter list cannot be called on an object; assigning name = name inside __init__ creates a local name and leaves the object empty; and a mutable value such as a list placed directly on the class is shared by every object made from it.
Try these next
- Add an
extend(extra)method that increases a recipe’s minutes, and call it on one of two recipes. - Print both objects after extending one, to prove the other is unchanged.
- Remove
selffrom a method’s parameter list and read the error before putting it back.
A timer you can extend
`__init__` and `describe` are ready. Add `extend(extra)` to increase the timer’s minutes, call it, and print the description again.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
When does __init__ run?
