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
Een klasseblok bovenaan met gedeelde methoden en het klasse-attribuut "kind"; daaronder twee instance-boxen die elk hun eigen "value"-attribuut bevatten, met pijlen van elke instance omhoog naar de klasse.

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.