5.21. Methods and attributes¶
A method is a function defined inside a class. The first
parameter is the instance the method is called on; the convention
is to name it self.
5.21.1. Instance methods¶
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def magnitude(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3, 4)
print(p.magnitude())
Output:
5.0
The call p.magnitude() passes p as self automatically.
Method bodies read and update attributes through self the
same way functions read and update names through parameters.
5.21.2. Instance vs class attributes¶
Attributes assigned to self – typically inside __init__
– belong to that one instance:
a = Point(1, 2)
b = Point(10, 20)
a.x = 99
print(a.x, b.x) # 99 10
a.x and b.x are different storage; mutating one does not
affect the other.
Names assigned in the class body, outside any method, are shared by every instance of the class:
class Counter:
kind = "tally" # class attribute -- shared
def __init__(self):
self.value = 0 # instance attribute -- per instance
a = Counter()
b = Counter()
print(a.kind, b.kind) # tally tally
Class attributes live on the class itself and are shared. Instance attributes live on each instance.¶
A class attribute is reached the same way as an instance one
(a.kind); Python looks first on the instance, then on the
class. Assigning to a.kind would create a new instance
attribute that shadows the class one, leaving b.kind
untouched.
Use class attributes for constants and defaults every instance should see the same way. Use instance attributes for state each object should own its own copy of.
5.21.3. __str__ and __repr__¶
Two special methods control how an instance prints. __str__
returns the “friendly” string used by print(); __repr__
returns the “developer” string used at the REPL and inside
container displays:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __repr__(self):
return "Point(" + str(self.x) + ", " + str(self.y) + ")"
p = Point(3, 4)
print(p) # uses __str__
print([p, p]) # uses __repr__ inside the list
Output:
(3, 4)
[Point(3, 4), Point(3, 4)]
If only __repr__ is defined, print() falls back to it
– so a single well-written __repr__ is usually enough.
Define __str__ as well only when the friendly form should
look different from the developer form. Aim for a __repr__
that looks like the call that would recreate the object;
debugging gets dramatically easier when printed values are
unambiguous.