2.21. Methoden en attributen¶
Een methode is een functie die binnen een klasse is gedefinieerd. De eerste parameter is de instance waarop de methode wordt aangeroepen; de conventie is om die self te noemen.
2.21.1. Instancemethoden¶
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())
Uitvoer:
5.0
De aanroep p.magnitude() geeft p automatisch door als self. Methode-bodies lezen en werken attributen bij via self op dezelfde manier waarop functies namen lezen en bijwerken via parameters.
2.21.2. Instance- versus klasse-attributen¶
Attributen die aan self worden toegewezen – doorgaans binnen __init__ – behoren tot die ene instance:
a = Point(1, 2)
b = Point(10, 20)
a.x = 99
print(a.x, b.x) # 99 10
a.x en b.x zijn verschillende opslag; de ene muteren heeft geen invloed op de andere.
Namen die in de body van de klasse worden toegewezen, buiten enige methode, worden gedeeld door elke instance van de klasse:
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
Klasse-attributen leven op de klasse zelf en worden gedeeld. Instance-attributen leven op elke instance.¶
Een klasse-attribuut wordt op dezelfde manier bereikt als een instance-attribuut (a.kind); Python kijkt eerst op de instance, daarna op de klasse. Toewijzen aan a.kind zou een nieuw instance-attribuut aanmaken dat het klasse-attribuut overschaduwt, terwijl b.kind onaangeroerd blijft.
Gebruik klasse-attributen voor constanten en standaardwaarden die elke instance op dezelfde manier moet zien. Gebruik instance-attributen voor toestand waarvan elk object zijn eigen kopie zou moeten bezitten.
2.21.3. __str__ en __repr__¶
Twee speciale methoden bepalen hoe een instance wordt afgedrukt. __str__ geeft de “vriendelijke” string terug die door print() wordt gebruikt; __repr__ geeft de “ontwikkelaars”-string terug die bij de REPL en binnen containerweergaven wordt gebruikt:
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
Uitvoer:
(3, 4)
[Point(3, 4), Point(3, 4)]
Als alleen __repr__ is gedefinieerd, valt print() daarop terug – dus een enkele goed geschreven __repr__ is meestal voldoende. Definieer __str__ er alleen bij wanneer de vriendelijke vorm er anders uit zou moeten zien dan de ontwikkelaarsvorm. Streef naar een __repr__ die eruitziet als de aanroep die het object opnieuw zou aanmaken; debuggen wordt dramatisch eenvoudiger wanneer afgedrukte waarden ondubbelzinnig zijn.