Ich versuche, einen Abschluss in Python 2.6 zu implementieren, und ich muss auf eine nicht lokale Variable zugreifen, aber es scheint, dass dieses Schlüsselwort in Python 2.x nicht verfügbar ist. Wie sollte man in Abschlüssen in diesen Python-Versionen auf nichtlokale Variablen in Closures zugreifen?
117
def inner(): print d; d = {'y': 1}
. Hierprint d
liest Outerd
und erzeugt so eine nichtlokale Variabled
im inneren Bereich.X = 1
bindet der Ausdruck den Namen einfach anX
ein bestimmtes Objekt (und anint
den Wert1
).X = 1; Y = X
bindet zwei Namen an genau dasselbe Objekt. Auf jeden Fall sind einige Objekte veränderbar und Sie können ihre Werte ändern.Die folgende Lösung ist von der Antwort von Elias Zamaria inspiriert , behandelt jedoch im Gegensatz zu dieser Antwort mehrere Aufrufe der äußeren Funktion korrekt. Die "Variable"
inner.y
ist lokal für den aktuellen Aufruf vonouter
. Nur ist es keine Variable, da dies verboten ist, sondern ein Objektattribut (das Objekt ist die Funktioninner
selbst). Dies ist sehr hässlich (beachten Sie, dass das Attribut erst erstellt werden kann, nachdem dieinner
Funktion definiert wurde), scheint jedoch effektiv zu sein.quelle
inc()
und a, diedec()
von außen zurückgegeben werden, um einen gemeinsam genutzten Zähler zu erhöhen und zu verringern. Dann müssen Sie entscheiden, an welche Funktion der aktuelle Zählerwert angehängt werden soll, und diese Funktion von den anderen referenzieren. Was etwas seltsam und asymmetrisch aussieht. ZB indec()
einer Zeile wieinc.value -= 1
.Anstelle eines Wörterbuchs gibt es weniger Unordnung in einer nichtlokalen Klasse . Beispiel von @ ChrisB ändern :
Dann
Jeder äußere () Aufruf ruft eine neue und eindeutige Klasse namens Kontext auf (nicht nur eine neue Instanz). So wird vermieden, dass @ Nathaniel sich vor dem gemeinsamen Kontext hütet .
quelle
__slots__ = ()
und Erstellen eines Objekts stattdessen die Klasse, zBcontext.z = 3
wäre ein erhöhenAttributeError
. Es ist für alle Klassen möglich, es sei denn, sie erben von einer Klasse, die keine Slots definiert.Ich denke, der Schlüssel hier ist, was Sie unter "Zugang" verstehen. Es sollte kein Problem beim Lesen einer Variablen außerhalb des Schließungsbereichs geben, z.
sollte wie erwartet funktionieren (Drucken 3). Das Überschreiben des Werts von x funktioniert jedoch nicht, z.
wird weiterhin gedruckt 3. Nach meinem Verständnis von PEP-3104 ist dies das, was das nichtlokale Schlüsselwort abdecken soll. Wie im PEP erwähnt, können Sie eine Klasse verwenden, um dasselbe zu erreichen (etwas chaotisch):
quelle
def ns(): pass
gefolgt vonns.x = 3
. Es ist nicht schön, aber für mein Auge etwas weniger hässlich.class Namespace: x = 3
?ns
ist ein globales Objekt, weshalb Siens.x
in derprint
Anweisung ganz am Ende auf Modulebene verweisen können .Es gibt eine andere Möglichkeit, nichtlokale Variablen in Python 2 zu implementieren, falls eine der Antworten hier aus irgendeinem Grund unerwünscht ist:
Es ist überflüssig, den Namen der Funktion in der Zuweisungsanweisung der Variablen zu verwenden, aber es sieht für mich einfacher und sauberer aus, als die Variable in ein Wörterbuch aufzunehmen. Der Wert wird von einem Anruf zum anderen gespeichert, genau wie in der Antwort von Chris B.
quelle
f = outer()
und später tung = outer()
, wirdf
der Zähler zurückgesetzt. Dies liegt daran, dass beide eine einzelneouter.y
Variable gemeinsam nutzen und nicht jede ihre eigene unabhängige Variable hat. Obwohl dieser Code ästhetisch ansprechender aussieht als die Antwort von Chris B, scheint sein Weg der einzige Weg zu sein, das lexikalische Scoping zu emulieren, wenn Sieouter
mehr als einmal anrufen möchten .outer.y
beinhaltet nichts Lokales für den Funktionsaufruf (Instanz)outer()
, sondern weist ein Attribut des Funktionsobjekts zu, das an den Namenouter
in seinem umschließenden Bereich gebunden ist . Und deshalb könnte man genauso gut verwendet hat, schriftlichouter.y
, andere Namen stattouter
, sofern es bekannt ist , in diesem Umfang gebunden zu sein. Ist das richtig?outer.y
Verwenden Sie stattdessen den Nameninner.y
(da erinner
innerhalb des Aufrufs gebunden istouter()
, was genau der gewünschte Bereich ist), sondern setzen die Initialisierunginner.y = 0
nach der Definition von inner (da das Objekt existieren muss, wenn sein Attribut erstellt wird), aber natürlich vorherreturn inner
?Hier ist etwas, das von einem Vorschlag inspiriert wurde, den Alois Mahdal in einem Kommentar zu einer anderen Antwort gemacht hat :
Aktualisieren
Nachdem ich kürzlich darauf zurückgeblickt hatte, war ich beeindruckt, wie dekoratorisch es war - als mir klar wurde, dass die Implementierung als solche es allgemeiner und nützlicher machen würde (obwohl dies die Lesbarkeit in gewissem Maße beeinträchtigt).
Beachten Sie, dass beide Versionen sowohl in Python 2 als auch in Python 3 funktionieren.
quelle
Die Scoping-Regeln von Python enthalten eine Warze. Durch die Zuweisung wird eine Variable lokal für den unmittelbar umschließenden Funktionsumfang. Für eine globale Variable würden Sie dies mit dem
global
Schlüsselwort lösen .Die Lösung besteht darin, ein Objekt einzuführen, das von den beiden Bereichen gemeinsam genutzt wird und veränderbare Variablen enthält, auf das jedoch selbst über eine nicht zugewiesene Variable verwiesen wird.
Eine Alternative ist einige Bereiche Hackery:
Möglicherweise können Sie einige Tricks herausfinden, um den Namen des Parameters zu ermitteln
outer
, und ihn dann als Variablennamen übergeben, ohne sich jedoch auf den Namen verlassen zuouter
müssen, müssen Sie einen Y-Kombinator verwenden.quelle
nonlocal
.locals()
Erstellt ein Wörterbuch vonouter()
s Einheimischen zu dem Zeitpunktinner()
definiert ist, aber das Ändern dieses Wörterbuchs ändert nicht dasv
inouter()
. Dies funktioniert nicht mehr, wenn Sie mehr innere Funktionen haben, die eine geschlossene Variable gemeinsam nutzen möchten. Sagen Sie ainc()
unddec()
erhöhen und verringern Sie einen gemeinsam genutzten Zähler.nonlocal
ist eine Python 3-Funktion.nonlocal
in Python 2 im Allgemeinen erzielt werden kann . Ihre Ideen decken nicht den allgemeinen Fall ab, sondern nur den mit einer inneren Funktion. Schauen Sie sich dieses Wesentliche als Beispiel an. Beide inneren Funktionen haben einen eigenen Container. Sie benötigen ein veränderbares Objekt im Rahmen der äußeren Funktion, wie bereits in anderen Antworten vorgeschlagen.nonlocal
in Python 3 eingeführte Schlüsselwort.Ein anderer Weg, dies zu tun (obwohl es zu ausführlich ist):
quelle
Die Erweiterung der eleganten Lösung von Martineau oben auf einen praktischen und etwas weniger eleganten Anwendungsfall, den ich bekomme:
quelle
Verwenden Sie eine globale Variable
Persönlich mag ich die globalen Variablen nicht. Mein Vorschlag basiert jedoch auf der Antwort https://stackoverflow.com/a/19877437/1083704
Hier muss der Benutzer
ranks
jedes Mal eine globale Variable deklarieren , wenn Sie die aufrufen müssenreport
. Durch meine Verbesserung muss der Benutzer die Funktionsvariablen nicht mehr initialisieren.quelle
inner
, sie jedoch nicht zuweisen, aber Sie können ihre Schlüssel und Werte ändern. Dies vermeidet die Verwendung globaler Variablen.