Einführung
Verschiedene Teile des Verhaltens von Python können über spezielle oder sogenannte "magische" Methoden angepasst werden. In diesem Abschnitt wird diese Idee vorgestellt. Darüber hinaus werden dynamischer Attributzugriff und gebundene Methoden diskutiert.
Einführung
Klassen können spezielle Methoden definieren. Diese haben eine besondere Bedeutung für den Python-Interpreter. Sie werden immer von __ vor und nachgestellt. Beispielsweise __init__.
class Stock(object):
def __init__(self):
...
def __repr__(self):
...
Es gibt Dutzende von speziellen Methoden, aber wir werden uns nur einige spezifische Beispiele ansehen.
Spezielle Methoden für Stringkonvertierungen
Objekte haben zwei Stringdarstellungen.
>>> from datetime import date
>>> d = date(2012, 12, 21)
>>> print(d)
2012-12-21
>>> d
datetime.date(2012, 12, 21)
>>>
Die str()-Funktion wird verwendet, um eine hübsche ausdruckbare Ausgabe zu erzeugen:
>>> str(d)
'2012-12-21'
>>>
Die repr()-Funktion wird verwendet, um eine detailliertere Darstellung für Programmierer zu erzeugen.
>>> repr(d)
'datetime.date(2012, 12, 21)'
>>>
Diese Funktionen, str() und repr(), verwenden eine Reihe spezieller Methoden in der Klasse, um den anzuzeigenden String zu erzeugen.
class Date(object):
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
## Wird mit `str()` verwendet
def __str__(self):
return f'{self.year}-{self.month}-{self.day}'
## Wird mit `repr()` verwendet
def __repr__(self):
return f'Date({self.year},{self.month},{self.day})'
Hinweis: Die Konvention für __repr__() besteht darin, einen String zurückzugeben, der, wenn an eval() übergeben wird, das zugrunde liegende Objekt wiederherstellt. Wenn dies nicht möglich ist, wird stattdessen eine leicht lesbare Darstellung verwendet.
Spezielle Methoden für Mathematik
Mathematische Operatoren beinhalten Aufrufe der folgenden Methoden.
a + b a.__add__(b)
a - b a.__sub__(b)
a * b a.__mul__(b)
a / b a.__truediv__(b)
a // b a.__floordiv__(b)
a % b a.__mod__(b)
a << b a.__lshift__(b)
a >> b a.__rshift__(b)
a & b a.__and__(b)
a | b a.__or__(b)
a ^ b a.__xor__(b)
a ** b a.__pow__(b)
-a a.__neg__()
~a a.__invert__()
abs(a) a.__abs__()
Spezielle Methoden für den Zugriff auf Elemente
Dies sind die Methoden, um Container zu implementieren.
len(x) x.__len__()
x[a] x.__getitem__(a)
x[a] = v x.__setitem__(a,v)
del x[a] x.__delitem__(a)
Sie können sie in Ihren Klassen verwenden.
class Sequence:
def __len__(self):
...
def __getitem__(self,a):
...
def __setitem__(self,a,v):
...
def __delitem__(self,a):
...
Methodenaufruf
Das Aufrufen einer Methode ist ein zweistufiger Prozess.
- Suche: Der
.-Operator - Methodenaufruf: Der
()-Operator
>>> s = stock.Stock('GOOG',100,490.10)
>>> c = s.cost ## Suche
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c() ## Methodenaufruf
49010.0
>>>
Gebundene Methoden
Eine Methode, die noch nicht vom Funktionsaufrufoperator () aufgerufen wurde, wird als gebundene Methode bezeichnet. Sie operiert auf der Instanz, aus der sie stammt.
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s
<Stock object at 0x590d0>
>>> c = s.cost
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()
49010.0
>>>
Gebundene Methoden sind oft die Quelle von sorglosen, nicht offensichtlichen Fehlern. Beispielsweise:
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> print('Cost : %0.2f' % s.cost)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: float argument required
>>>
Oder von absichtsvollen Verhaltensweisen, die schwer zu debuggen sind.
f = open(filename, 'w')
...
f.close ## Ups, hat überhaupt nichts getan. `f` ist immer noch geöffnet.
In beiden Fällen ist der Fehler darauf zurückzuführen, dass man die abschließenden Klammern vergessen hat. Beispielsweise s.cost() oder f.close().
Attributzugriff
Es gibt eine alternative Möglichkeit, Attribute zuzugreifen, zu manipulieren und zu verwalten.
getattr(obj, 'name') ## Entspricht obj.name
setattr(obj, 'name', value) ## Entspricht obj.name = value
delattr(obj, 'name') ## Entspricht del obj.name
hasattr(obj, 'name') ## Testet, ob das Attribut existiert
Beispiel:
if hasattr(obj, 'x'):
x = getattr(obj, 'x'):
else:
x = None
*Hinweis: getattr() hat auch einen nützlichen Standardwert *arg*.
x = getattr(obj, 'x', None)
Übung 4.9: Bessere Ausgabe bei der Ausgabe von Objekten
Ändern Sie das Stock-Objekt, das Sie in stock.py definiert haben, so, dass die __repr__()-Methode eine nützlichere Ausgabe erzeugt. Beispielsweise:
>>> goog = stock.Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>
Sehen Sie sich an, was passiert, wenn Sie ein Portfolio von Aktien lesen und die resultierende Liste betrachten, nachdem Sie diese Änderungen vorgenommen haben. Beispielsweise:
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> portfolio
... sehen Sie sich an, was die Ausgabe ist...
>>>
Übung 4.10: Ein Beispiel für die Verwendung von getattr()
getattr() ist ein alternatives Mechanismus zum Lesen von Attributen. Es kann verwendet werden, um extrem flexiblen Code zu schreiben. Beginnen Sie mit diesem Beispiel:
>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name','shares']
>>> for colname in columns:
print(colname, '=', getattr(s, colname))
name = GOOG
shares = 100
>>>
Beobachten Sie genau, dass die Ausgabedaten vollständig durch die in der columns-Variable aufgelisteten Attributnamen bestimmt werden.
In der Datei tableformat.py nehmen Sie diese Idee und erweitern Sie sie zu einer verallgemeinerten Funktion print_table(), die eine Tabelle druckt, die benutzerdefinierte Attribute einer Liste beliebiger Objekte zeigt. Wie bei der früheren print_report()-Funktion sollte print_table() auch eine TableFormatter-Instanz akzeptieren, um das Ausgabeformat zu steuern. So sollte es funktionieren:
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
name shares
---------- ----------
AA 100
IBM 50
CAT 150
MSFT 200
GE 95
MSFT 50
IBM 100
>>> print_table(portfolio, ['name','shares', 'price'], formatter)
name shares price
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
>>>
Zusammenfassung
Herzlichen Glückwunsch! Sie haben das Labor zu den speziellen Methoden abgeschlossen. Sie können in LabEx weitere Labore absolvieren, um Ihre Fähigkeiten zu verbessern.