Why this matters
So far your data has lived in built-in types: numbers, strings, lists, dicts. Real programs model things (accounts, users, network connections, game pieces) that bundle data with behavior. You could pass a dict around plus a pile of functions that operate on it, but nothing ties them together or stops someone from misspelling a key. A class lets you define your own type: a template that says what data each object holds and what it can do. Every built-in type you have used is itself a class; this lesson shows you how to write your own and what Python actually does when you do.
A first class
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3, 4)
print(p.x, p.y)
print(p.distance_from_origin())
3 4
5.0
Piece by piece:
class Point:starts a class statement. The indented body defines what belongs to the class. Class names useCapWordsby convention.definside a class defines a method: a function that belongs to the class.Point(3, 4)calls the class, which creates a new object, an instance ofPoint, and then calls its__init__method to set it up.__init__(two underscores on each side, pronounced "dunder init") is the initializer. Its job is to put the starting data into the new object.selfis the instance being worked on.self.x = xcreates an attribute namedxon that particular object.p.xreads an attribute;p.distance_from_origin()calls a method.
What really happens when you call a class
Calling Point(3, 4) performs two steps:
Point(3, 4)
|
| 1. Point.__new__(Point) creates an empty instance -> obj
| 2. Point.__init__(obj, 3, 4) fills it in (must return None)
v
obj is returned and bound to the name p
You almost never write __new__; the default one allocates the object. __init__ does not create the object, it receives one that already exists, which is why it gets self and returns nothing.
self and bound methods
Where does self come from in p.distance_from_origin()? There is no argument in the parentheses. The rule: when you look up a function through an instance, Python returns a bound method that remembers the instance. Calling it passes the instance as the first argument. These two lines do the same thing:
p.distance_from_origin()
Point.distance_from_origin(p)
You can see the binding:
>>> p.distance_from_origin
<bound method Point.distance_from_origin of <__main__.Point object at 0x7f...>>
>>> Point.distance_from_origin
<function Point.distance_from_origin at 0x7f...>
self is not a keyword, just a universally followed convention for the first parameter. Name it anything else and your colleagues will be confused, but Python will not care.
Objects are namespaces backed by dicts
An instance stores its attributes in a dictionary called __dict__:
p = Point(3, 4)
print(p.__dict__)
p.label = "corner" # attributes can be added at any time
print(p.__dict__)
{'x': 3, 'y': 4}
{'x': 3, 'y': 4, 'label': 'corner'}
The class is an object too, with its own __dict__ holding the methods (and anything else defined in the class body):
print(type(p)) # <class '__main__.Point'>
print('distance_from_origin' in Point.__dict__) # True
print('distance_from_origin' in p.__dict__) # False
This is the key to attribute lookup. When you write p.name, Python (simplifying slightly):
1. looks in p.__dict__ found? use it
2. looks in type(p).__dict__ found? use it (functions become bound methods)
3. looks in the parent classes (inheritance, lesson 4)
4. raises AttributeError
So methods are shared: there is one distance_from_origin function stored in the class, and every instance finds it by falling through to step 2. Data assigned through self lands in each instance's own dict, so each point has its own x and y.
Assigning always writes to the instance: p.x = 10 puts x into p.__dict__, never into the class. This asymmetry (read falls through, write stays local) explains the lookup quiz in this lesson and an important pitfall in lesson 3.
Methods that change state
Methods can read and modify the instance's attributes. That is the point of bundling:
class Thermostat:
def __init__(self, target):
self.target = target
self.changes = 0
def adjust(self, delta):
self.target += delta
self.changes += 1
return self.target
t = Thermostat(20)
t.adjust(2)
t.adjust(-1)
print(t.target, t.changes)
21 2
Every instance has separate state:
a = Thermostat(18)
b = Thermostat(25)
a.adjust(1)
print(a.target, b.target) # 19 25
Classes are types
A class you write is a type exactly like int or list:
print(isinstance(p, Point)) # True
print(type(p) is Point) # True
print(type(3) is int) # True
print(type(Point)) # <class 'type'>
That last line says the class Point is itself an instance of type. In Python, classes are objects created at run time when the class statement executes: the body runs like ordinary code, its names are collected into a dict, and type builds the class object from them. Nothing about a class is fixed at compile time.
Common mistakes
Forgetting self in the method definition.
class Dog:
def bark():
return "woof"
Dog().bark()
TypeError: Dog.bark() takes 0 positional arguments but 1 was given
The "1 given" is the instance Python passed automatically. Add self as the first parameter.
Forgetting self. when using an attribute.
class Dog:
def __init__(self, name):
self.name = name
def greet(self):
return "I am " + name
NameError: name 'name' is not defined
Inside a method, attributes are reached through self. A bare name is a local or global variable, which does not exist here.
Misspelling __init__. def __int__(self, ...) or def _init_(self, ...) is silently just another method, so Point(3, 4) fails with TypeError: Point() takes no arguments.
Typos create new attributes. self.balence = 0 happily creates a misspelled attribute, and later self.balance raises AttributeError: 'Account' object has no attribute 'balance'. Read the attribute names in the error closely.
Recap
- A class defines a new type; calling it creates an instance and runs
__init__(self, ...)on it. selfis the instance;obj.method(args)isClass.method(obj, args)via a bound method.- Instance attributes live in
obj.__dict__; methods live in the class's__dict__. - Lookup reads instance first, then class (then parents); assignment always writes to the instance.
- Classes are objects themselves, created at run time, instances of
type.
Going further: in the REPL, create an instance and call vars(obj) (the same as obj.__dict__) and dir(obj) to list everything reachable from it. Read the tutorial chapter "Classes" at docs.python.org, sections 9.3 and 9.4.