Das Zen von Python besagt, dass es nur einen Weg geben sollte, Dinge zu tun - dennoch stoße ich häufig auf das Problem, zu entscheiden, wann eine Funktion verwendet werden soll und wann eine Methode verwendet werden soll.
Nehmen wir ein triviales Beispiel - ein ChessBoard-Objekt. Nehmen wir an, wir brauchen einen Weg, um alle legalen King-Moves auf dem Brett verfügbar zu machen. Schreiben wir ChessBoard.get_king_moves () oder get_king_moves (chess_board)?
Hier sind einige verwandte Fragen, die ich mir angesehen habe:
- Warum verwendet Python "magische Methoden"?
- Gibt es einen Grund, warum Python-Strings keine String-Längenmethode haben?
Die Antworten, die ich erhielt, waren größtenteils nicht schlüssig:
Warum verwendet Python Methoden für einige Funktionen (z. B. list.index ()), aber Funktionen für andere (z. B. len (list))?
Der Hauptgrund ist die Geschichte. Funktionen wurden für Operationen verwendet, die für eine Gruppe von Typen generisch waren und die auch für Objekte funktionieren sollten, die überhaupt keine Methoden hatten (z. B. Tupel). Es ist auch praktisch, eine Funktion zu haben, die leicht auf eine amorphe Sammlung von Objekten angewendet werden kann, wenn Sie die Funktionsmerkmale von Python verwenden (map (), apply () et al.).
Tatsächlich ist die Implementierung von len (), max (), min () als integrierte Funktion weniger Code als die Implementierung als Methoden für jeden Typ. Man kann über Einzelfälle streiten, aber es ist ein Teil von Python, und es ist zu spät, um jetzt solche grundlegenden Änderungen vorzunehmen. Die Funktionen müssen erhalten bleiben, um einen massiven Codebruch zu vermeiden.
Das oben Gesagte ist zwar interessant, sagt aber nicht viel darüber aus, welche Strategie zu verfolgen ist.
Dies ist einer der Gründe - bei benutzerdefinierten Methoden können Entwickler einen anderen Methodennamen auswählen, z. B. getLength (), length (), getlength () oder was auch immer. Python erzwingt eine strikte Benennung, damit die allgemeine Funktion len () verwendet werden kann.
Etwas interessanter. Ich gehe davon aus, dass Funktionen in gewisser Weise die pythonische Version von Schnittstellen sind.
Zuletzt von Guido selbst :
Als ich über die Fähigkeiten / Schnittstellen sprach, dachte ich über einige unserer speziellen Methodennamen nach. In der Sprachreferenz heißt es: "Eine Klasse kann bestimmte Operationen implementieren, die durch spezielle Syntax aufgerufen werden (z. B. arithmetische Operationen oder Subskription und Slicing), indem Methoden mit speziellen Namen definiert werden." Es gibt jedoch alle diese Methoden mit speziellen Namen wie
__len__
oder,__unicode__
die eher zum Nutzen integrierter Funktionen als zur Unterstützung der Syntax bereitgestellt werden. Vermutlich in einem schnittstellenbasierten Python würden diese Methoden zu regelmäßig benannten Methoden in einem ABC werden, so dass__len__
dies werden würdeclass container: ... def len(self): raise NotImplemented
Wenn ich noch etwas darüber nachdenke, verstehe ich nicht, warum alle syntaktischen Operationen nicht einfach die entsprechende normal benannte Methode für ein bestimmtes ABC aufrufen würden. "
<
" würde zum Beispiel vermutlich "object.lessthan
" (oder vielleicht "comparable.lessthan
") aufrufen . Ein weiterer Vorteil wäre die Möglichkeit, Python von dieser seltsamen Seltsamkeit abzusetzen, die mir als HCI-Verbesserung erscheint .Hm. Ich bin mir nicht sicher, ob ich damit einverstanden bin (Abbildung :-).
Es gibt zwei Teile der "Python-Begründung", die ich zuerst erläutern möchte.
Zunächst habe ich aus HCI-Gründen len (x) gegenüber x.len () gewählt (
def __len__()
kam viel später). Es gibt zwei miteinander verflochtene Gründe, beide HCI:(a) Bei einigen Operationen liest sich die Präfixnotation einfach besser als die Postfix - Präfix (und Infix!) - Operationen haben in der Mathematik eine lange Tradition. Sie mögen Notationen, bei denen die Grafik dem Mathematiker hilft, über ein Problem nachzudenken. Vergleichen Sie die einfach , mit dem wir eine Formel wie umschreiben
x*(a+b)
inx*a + x*b
auf die Ungeschicklichkeit der die gleiche Sache mit einer rohen OO - Notation.(b) Wenn ich Code lese, der besagt , dass
len(x)
ich weiß, dass er nach der Länge von etwas fragt. Dies sagt mir zwei Dinge: Das Ergebnis ist eine ganze Zahl, und das Argument ist eine Art Container. Im Gegenteil, wenn ich lesex.len()
, muss ich bereits wissen, dassx
es sich um eine Art Container handelt, der eine Schnittstelle implementiert oder von einer Klasse mit einem Standard erbtlen()
. Erleben Sie die Verwirrung, die wir gelegentlich haben, wenn eine Klasse, die kein Mapping implementiert, eineget()
oderkeys()
-Methode hat oder etwas, das keine Datei ist, einewrite()
Methode hat.Wenn ich dasselbe auf andere Weise sage, sehe ich 'len' als eine eingebaute Operation . Ich würde es hassen, das zu verlieren. Ich kann nicht sicher sagen, ob Sie das gemeint haben oder nicht, aber 'def len (self): ...' klingt sicherlich so, als ob Sie es auf eine gewöhnliche Methode herabstufen möchten. Da bin ich stark -1.
Das zweite Stück Python-Begründung, das ich zu erklären versprochen habe, ist der Grund, warum ich spezielle Methoden gewählt habe, um zu schauen
__special__
und nicht nurspecial
. Ich hatte viele Operationen erwartet, die Klassen möglicherweise überschreiben möchten, einige Standardoperationen (z. B.__add__
oder__getitem__
), andere nicht so Standardoperationen (z. B. Pickles hatten__reduce__
lange Zeit überhaupt keine Unterstützung in C-Code). Ich wollte nicht, dass diese speziellen Operationen gewöhnliche Methodennamen verwenden, da dann bereits vorhandene Klassen oder Klassen, die von Benutzern ohne enzyklopädischen Speicher für alle speziellen Methoden geschrieben wurden, versehentlich Operationen definieren würden, die sie nicht implementieren wollten mit möglicherweise katastrophalen Folgen. Ivan Krstić erklärte dies in seiner Nachricht, die eintraf, nachdem ich das alles aufgeschrieben hatte.- - Guido van Rossum (Homepage: http://www.python.org/~guido/ )
Mein Verständnis davon ist, dass in bestimmten Fällen die Präfixnotation einfach sinnvoller ist (dh Duck.quack ist aus sprachlicher Sicht sinnvoller als Quacksalber (Duck)). Auch hier ermöglichen die Funktionen "Schnittstellen".
In einem solchen Fall würde ich raten, get_king_moves nur basierend auf Guidos erstem Punkt zu implementieren. Es bleiben jedoch noch viele offene Fragen bezüglich der Implementierung einer Stack- und Queue-Klasse mit ähnlichen Push- und Pop-Methoden - sollten es Funktionen oder Methoden sein? (hier würde ich Funktionen erraten, weil ich wirklich eine Push-Pop-Schnittstelle signalisieren möchte)
TLDR: Kann jemand erklären, wie die Strategie für die Entscheidung, wann Funktionen oder Methoden verwendet werden sollen, aussehen sollte?
X.frob
oderX.__frob__
und freistehend habenfrob
.