Was ist die pythonischste und korrekteste Art, Kompositionsaliasnamen zu erstellen?
Hier ist ein hypothetisches Szenario:
class House:
def cleanup(self, arg1, arg2, kwarg1=False):
# do something
class Person:
def __init__(self, house):
self.house = house
# aliases house.cleanup
# 1.
self.cleanup_house = self.house.cleanup
# 2.
def cleanup_house(self, arg1, arg2, kwarg1=False):
return self.house.cleanup(arg1=arg1, arg2=arg2, kwarg1=kwarg1)
AFAIK mit # 1 meine getesteten Editoren verstehen diese genauso gut wie # 2 - automatische Vervollständigung, Dokumentzeichenfolgen usw.
Gibt es irgendwelche Nachteile bei # 1 Ansatz? Welcher Weg ist aus Pythons Sicht korrekter?
Eine Erweiterung der Methode Nr. 1, die nicht einstellbar und vom Typ angedeutet ist, wäre immun gegen alle in den Kommentaren genannten Probleme:
class House:
def cleanup(self, arg1, arg2, kwarg1=False):
"""clean house is nice to live in!"""
pass
class Person:
def __init__(self, house: House):
self._house = house
# aliases
self.cleanup_house = self.house.cleanup
@property
def house(self):
return self._house
Person.cleanup_house
. Es bekommt das Haus, das mit dieser Person verbunden ist, und räumt es auf. # 1 wird ein Haus aufräumen, und zu einem bestimmten Zeitpunkt (Initialisierung) war dieses Haus das richtige. Es geht nicht gut mit der Person, die das Haus wechselt, oder wenn eine Person kein Haus hatAntworten:
Bei der ersten Methode gibt es eine Reihe von Problemen:
house
eineproperty
mit einem Setter machen, aber das ist keine triviale Arbeit für etwas, das es nicht erfordern sollte. Am Ende dieser Antwort finden Sie eine Beispielimplementierung.cleanup_house
wird nicht vererbbar sein. Ein in einer Klasse definiertes Funktionsobjekt ist ein Nicht-Daten-Deskriptor, der vererbt und überschrieben sowie an eine Instanz gebunden werden kann. Ein Instanzattribut wie im ersten Ansatz ist in der Klasse überhaupt nicht vorhanden. Die Tatsache, dass es sich um eine gebundene Methode handelt, ist zufällig. Eine untergeordnete Klasse kann beispielsweise nicht darauf zugreifensuper().cleanup_house
.person.cleanup_house.__name__ != 'cleanup_house'
. Dies überprüfen Sie nicht oft, aber wenn Sie dies tun, erwarten Sie, dass der Funktionsname lautetcleanup
.Die gute Nachricht ist, dass Sie Signaturen nicht mehrmals wiederholen müssen, um Ansatz 2 zu verwenden. Python bietet die sehr praktische Notation splat (
*
) / splatty-splat (**
) zum Delegieren aller Argumentprüfungen an die zu verpackende Methode:Und das ist es. Alle regulären und Standardargumente werden unverändert übergeben.
Dies ist der Grund, warum # 2 bei weitem der pythonischere Ansatz ist. Ich habe keine Ahnung, wie es mit Editoren interagieren wird, die Typhinweise unterstützen, es sei denn, Sie kopieren die Methodensignatur.
Eine Sache, die ein Problem sein kann, ist, dass
cleanup_house.__doc__
es nicht dasselbe ist wiehouse.cleanup.__doc__
. Dies könnte möglicherweise eine Konvertierung vonhouse
a verdienenproperty
, deren Setter zuweistcleanup_house.__doc__
.Um das Problem 1. (aber nicht 2. oder 3.) zu beheben, können Sie es
house
als Eigenschaft mit einem Setter implementieren . Die Idee ist, die Aliase zu aktualisieren, wenn sich dashouse
Attribut ändert. Dies ist im Allgemeinen keine gute Idee, aber hier ist eine alternative Implementierung zu dem, was Sie in der Frage haben, die wahrscheinlich ein bisschen besser funktionieren wird:quelle
*args, **kwargs
schwierig, mit Funktionen zur automatischen Vervollständigung von Unterbrechungen zu arbeiten. Sie müssen Docstrings auch zweimal definierenhouse.cleanup
und /person.cleanup_house
oder irgendwie hacken. Bei diesem Ansatz müssen Sie die Signaturen und Dokumentzeichenfolgen ständig manuell verwalten, was in großen Programmen eine Menge Arbeit bedeuten kann.*args
und bin**kwargs
umständlich. Sie sind buchstäblich da, sodass Sie keine Unterschriften pflegen müssen.__doc__
kann durch direkte Zuordnung gepflegt werden.functool.wraps(<parent_func>)
Dekorateur, obwohl das einleitet als auch viele Ungereimtheiten.Ich wollte hier nur einen weiteren Ansatz hinzufügen: Wenn Sie
house
öffentlich und einstellbar sein möchten (ich würde so etwas im Allgemeinen als unveränderlich behandeln), können Siecleanup_house
die Immobilie wie folgt gestalten:Zumindest in einer Ipython-REPL scheinen die Code-Vervollständigung und die Dokumentzeichenfolge so zu funktionieren, wie Sie es sich erhoffen. Beachten Sie, wie es mit Typanmerkungen interagieren würde ...
EDIT: Also, mypy 0.740 kann zumindest nicht auf die Typensignatur von schließen
person.cleanup_house
, also ist das nicht großartig, obwohl es nicht überraschend ist:Ich würde immer noch einfach mit # 2 gehen.
quelle