So bin ich neugierig auf die Ansichten von erfahrenen Python-Programmierer auf die folgende Stil Frage. Angenommen, ich baue eine Funktion, die Zeile für Zeile durch einen Pandas-Datenrahmen oder einen ähnlichen Anwendungsfall iteriert, in dem eine Funktion Zugriff auf ihren vorherigen Zustand benötigt. Es scheint mindestens vier Möglichkeiten zur Umsetzung dieses in Python zu sein:Idiome in Python: Schließung vs Funktor vs Objekt
1) Verschlüsse:
def outer():
previous_state = None
def inner(current_state) :
nonlocal previous_state
#do something
previous_state=current_state
return something
Also, wenn Sie von einem JavaScript-Hintergrund kommen wird dies zweifellos natürlich erscheinen zu Ihnen. Es fühlt sich auch in Python ziemlich natürlich an, bis Sie auf den umschließenden Bereich zugreifen müssen, wenn Sie am Ende etwas wie inner.__code__.co_freevars
tun werden, das Ihnen die Namen Ihrer einschließenden Variablen als Tupel und den Index des einen geben wird Sie wollen und gehen dann zu inner.__closure__[index].cell_contents
, um seinen Wert zu erhalten. Nicht gerade elegant, aber ich nehme an, es geht oft darum, den Umfang zu verbergen, also ist es sinnvoll, dass es schwer zu erreichen ist. Auf der anderen Seite fühlt es sich ein wenig komisch an, dass Python die umschließende Funktion privat macht, wenn es fast jede andere Möglichkeit, eine private Variable im Vergleich zu OOP-Sprachen zu haben, beseitigt hat.
2) Functor
def outer():
def inner(current_state):
#do something
inner.previous_state=current_state
return something
ret = inner
ret.previous_state=None
return ret
Dieses „öffnet den Verschluss“, dass jetzt der umschließenden Zustand vollständig sichtbar als ein Attribut der Funktion. Das funktioniert, weil Funktionen wirklich nur getarnte Objekte sind. Ich lehne mich dazu als das pythischste an. Es ist klar, prägnant und lesbar.
3) Objekte Dies ist wahrscheinlich die am meisten vertraut OOP-Programmierer
class Calculator(Object) :
def __init__(self):
self.previous_state=None
def do_something(self, current_state) :
#do_something
self.previous_state = current_state
return something
Der größte con ist, dass Sie mit einer Menge von Klassendefinitionen, um am Ende neigen. Das ist in einer vollständigen OOP-Sprache wie Java gut, wo Sie Interfaces und Ähnliches haben, um das zu managen, aber in einer entarteten Sprache scheint es ein bisschen seltsam zu sein, viele einfache Klassen zu haben, nur um eine Funktion mit etwas Status zu transportieren.
4) Globals - Ich werde das nicht zeigen, wie ich den globalen Namen verschmutzen Raum
5) Dekorateure speziell vermeiden wollen - das ist ein bisschen ein Curveball, aber Sie können Dekorateure benutzen teilweise zu speichern, Zustandsinformationen.
@outer
def inner(previous_state, current_state):
#do something
return something
def outer(inner) :
def wrapper(current_state) :
result = inner(wrapper.previous_state, current_state)
wrapper.previous_state = current_state
return result
ret = wrapper
ret.previous_state=None
return result
Diese Art der Syntax ist die am wenigsten mir bekannt vor, aber wenn ich
func = inner
ich nenne jetzt eigentlich
func = outer(inner)
und dann func()
wirkt ebenso wie die Funktors wiederholt aufrufen Beispiel. Ich hasse diesen Weg wirklich am meisten. Es scheint mir eine wirklich intransparente Syntax zu haben, in der es nicht klar ist, ob der Aufruf von inner (current_state) viele Male das selbe Ergebnis liefert oder ob es Ihnen jedes Mal eine neu dekorierte Funktion gibt, also scheint es eine schlechte Übung zu sein Dekoratoren, die auf diese Weise einen Zustand zu einer Funktion hinzufügen.
Also was ist der richtige Weg? Welche Vor- und Nachteile habe ich hier verpasst?
Ich bin nicht ganz folgen, warum Sie denken, Sie müssen auf geschlossene Variablen zugreifen über 'innere .__ closure__'; Namen enden nur in dieser Struktur, weil Sie sie aktiv in der inneren Funktion * schon * verwenden. Die '__closure__'-Struktur ist wirklich ein internes Implementierungsdetail. –
Ich lasse die Leute das entwickeln, aber aus Erfahrung neigen Python-Entwickler dazu, die Klasse für diese Art von Szenario zu verwenden. Oder Dekorateure, wenn Sie keine volle Klarheit benötigen und die nette Syntax mit '@' bevorzugen. Sie möchten kein nichtlokales oder globales Schlüsselwort verwenden. Und Funktor ist oft offen für die Schließung. – Cyrbil
Geschlossene Namen unterscheiden sich in dieser Hinsicht nicht von Einheimischen; Sie kommen normalerweise nicht in die Lage, ihre Einheimischen zu lesen, warum tun Sie das Gleiche mit den Schließungen? Wenn Sie diesen Status für die Verwendung außerhalb der Funktion verfügbar machen müssen, verwenden Sie den Funktor- oder Klassenansatz. –