2.21. Methoden und Attribute¶
Eine Methode ist eine innerhalb einer Klasse definierte Funktion. Der erste Parameter ist die Instanz, auf der die Methode aufgerufen wird; üblicherweise wird er self genannt.
2.21.1. Instanzmethoden¶
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())
Ausgabe:
5.0
Der Aufruf p.magnitude() übergibt p automatisch als self. Methodenrümpfe lesen und aktualisieren Attribute über self auf dieselbe Weise, wie Funktionen Namen über Parameter lesen und aktualisieren.
2.21.2. Instanz- vs. Klassenattribute¶
Attribute, die self zugewiesen werden – typischerweise innerhalb von __init__ –, gehören zu dieser einen Instanz:
a = Point(1, 2)
b = Point(10, 20)
a.x = 99
print(a.x, b.x) # 99 10
a.x und b.x sind unterschiedliche Speicherplätze; das Verändern des einen wirkt sich nicht auf das andere aus.
Namen, die im Klassenrumpf zugewiesen werden, außerhalb jeder Methode, werden von jeder Instanz der Klasse gemeinsam genutzt:
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
Klassenattribute leben auf der Klasse selbst und werden gemeinsam genutzt. Instanzattribute leben auf jeder Instanz.¶
Ein Klassenattribut wird auf dieselbe Weise erreicht wie ein Instanzattribut (a.kind); Python sucht zuerst auf der Instanz, dann auf der Klasse. Eine Zuweisung an a.kind würde ein neues Instanz-Attribut erstellen, das das Klassenattribut überdeckt, während b.kind unberührt bleibt.
Verwenden Sie Klassenattribute für Konstanten und Standardwerte, die jede Instanz auf dieselbe Weise sehen soll. Verwenden Sie Instanzattribute für Zustand, von dem jedes Objekt seine eigene Kopie besitzen soll.
2.21.3. __str__ und __repr__¶
Zwei spezielle Methoden steuern, wie eine Instanz ausgegeben wird. __str__ gibt die „freundliche“ Zeichenkette zurück, die von print() verwendet wird; __repr__ gibt die „Entwickler“-Zeichenkette zurück, die in der REPL und innerhalb von Container-Darstellungen verwendet wird:
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
Ausgabe:
(3, 4)
[Point(3, 4), Point(3, 4)]
Wenn nur __repr__ definiert ist, fällt print() darauf zurück – ein einzelnes, gut geschriebenes __repr__ reicht also meist aus. Definieren Sie __str__ zusätzlich nur dann, wenn die freundliche Form anders aussehen soll als die Entwicklerform. Streben Sie ein __repr__ an, das wie der Aufruf aussieht, der das Objekt neu erstellen würde; das Debuggen wird dramatisch einfacher, wenn ausgegebene Werte eindeutig sind.