Anpassen des dynamischen Verhaltens von Python

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

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.

  1. Suche: Der .-Operator
  2. 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...
>>>
✨ Lösung prüfen und üben

Ü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
>>>
✨ Lösung prüfen und üben

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.