Kannst du Verschlüsse erklären (wie sie sich auf Python beziehen)?

Ich habe viel über Verschlüsse gelesen und ich glaube, ich verstehe sie, aber ohne das Bild für mich und andere zu zerreißen, hoffe ich, dass jemand die Schließungen so prägnant und klar wie möglich erklären kann. Ich suche eine einfache Erklärung, die mir helfen könnte, zu verstehen, wo und warum ich sie benutzen möchte.

    10 Solutions collect form web for “Kannst du Verschlüsse erklären (wie sie sich auf Python beziehen)?”

    Verschluss auf Verschluss

    Objekte sind Daten mit Methoden angehängt, Schließungen sind Funktionen mit angehängten Daten.

    def make_counter(): i = 0 def counter(): # counter() is a closure nonlocal i i += 1 return i return counter c1 = make_counter() c2 = make_counter() print (c1(), c1(), c2(), c2()) # -> 1 2 1 2 

    Es ist einfach: Eine Funktion, die Variablen aus einem enthaltenden Bereich verweist, potenziell nach Ablauf der Kontrolle hat diesen Bereich verlassen. Das letzte Bit ist sehr nützlich:

     >>> def makeConstantAdder(x): ... constant = x ... def adder(y): ... return y + constant ... return adder ... >>> f = makeConstantAdder(12) >>> f(3) 15 >>> g = makeConstantAdder(4) >>> g(3) 7 

    Beachten Sie, dass 12 und 4 "verschwunden" in f und g, diese Funktion ist, was machen f und g ordnungsgemäße Schließungen.

    Ich mag diese grobe, prägnante Definition :

    Eine Funktion, die sich auf Umgebungen beziehen kann, die nicht mehr aktiv sind.

    Ich würde hinzufügen

    Ein Verschluss ermöglicht es Ihnen, Variablen in eine Funktion zu binden, ohne sie als Parameter zu übergeben .

    Dekorateure, die Parameter akzeptieren, sind eine gemeinsame Verwendung für Verschlüsse. Verschlüsse sind ein gemeinsamer Umsetzungsmechanismus für diese Art von "Funktionsfabrik". Ich wähle häufig die Schließung im Strategy Pattern, wenn die Strategie zur Laufzeit um Daten geändert wird.

    In einer Sprache, die eine anonyme Blockdefinition erlaubt – z. B. Ruby, können C # – Verschlüsse verwendet werden, um neuartige Steuerstrukturen zu implementieren. Der Mangel an anonymen Blöcken gehört zu den Einschränkungen der Schließungen in Python .

    Um ehrlich zu sein, verstehe ich Verschlüsse perfekt, außer ich habe noch nie klar, was genau ist die Sache, die die "Schließung" und was ist so "Schließung" darüber. Ich empfehle Ihnen aufgeben auf der Suche nach einer Logik hinter der Wahl des Begriffs.

    Wie auch immer, hier ist meine Erklärung:

     def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5 

    Eine wichtige Idee hierbei ist, dass das von Foo zurückgegebene Funktionsobjekt einen Haken zum lokalen var 'x' beibehält, obwohl 'x' aus dem Geltungsbereich gegangen ist und verstorben sein sollte. Dieser Haken ist der var selbst, nicht nur der Wert, den var zu der Zeit hatte, also wenn bar genannt wird, druckt er 5, nicht 3.

    Auch klar, dass Python 2.x beschränkte Schließung hat: Es gibt keine Möglichkeit, dass ich 'x' in 'bar' ändern kann, weil das Schreiben 'x = bla' ein lokales 'x' in bar deklarieren würde, nicht zu 'x' von foo zuweisen . Dies ist eine Nebenwirkung von Pythons Aufgabe = Deklaration. Um dies zu umgehen, stellt Python 3.0 das nichtlokale Schlüsselwort vor:

     def foo(): x = 3 def bar(): print x def ack(): nonlocal x x = 7 x = 5 return (bar, ack) bar, ack = foo() ack() # modify x of the call to foo bar() # print 7 

    Ich habe noch nie davon gehört, dass Transaktionen im selben Kontext verwendet werden, um zu erklären, was eine Schließung ist und es gibt hier eigentlich keine Transaktions-Semantik.

    Es heißt eine Schließung, weil es die äußere Variable (konstant) "schließt" – dh es ist nicht nur eine Funktion, sondern ein Umfeld der Umgebung, in der die Funktion erstellt wurde.

    Im folgenden Beispiel wird das Aufrufen des Verschlusses g nach dem Ändern von x auch den Wert von x innerhalb von g ändern, da g über x schließt:

     x = 0 def f(): def g(): return x * 2 return g closure = f() print(closure()) # 0 x = 2 print(closure()) # 4 

    Hier ist ein typischer Anwendungsfall für Verschlüsse – Rückrufe für GUI-Elemente (dies wäre eine Alternative zur Unterklassen der Button-Klasse). Zum Beispiel können Sie eine Funktion aufbauen, die als Antwort auf eine Schaltflächen-Taste aufgerufen wird, und "schließen" über die relevanten Variablen im übergeordneten Bereich, die für die Bearbeitung des Klicks notwendig sind. Auf diese Weise können Sie ziemlich komplizierte Schnittstellen von der gleichen Initialisierungsfunktion abschließen und alle Abhängigkeiten in die Schließung aufbauen.

    In Python ist eine Schließung eine Instanz einer Funktion, die Variablen an sie unveränderlich gebunden hat.

    In der Tat, das Datenmodell erklärt dies in seiner Beschreibung der Funktionen ' __closure__ Attribut:

    Keine oder ein Tupel von Zellen , die Bindungen für die freien Variablen der Funktion enthalten. Schreibgeschützt

    Um dies zu demonstrieren:

     def enclosure(foo): def closure(bar): print(foo, bar) return closure closure_instance = enclosure('foo') 

    Klar, wir wissen, dass wir nun eine Funktion aus dem Variablennamen closure_instance . Scheinbar, wenn wir es mit einem Objekt, bar , sollte es den String, 'foo' und was auch immer die String-Darstellung von bar ausdrucken.

    Tatsächlich ist die Zeichenfolge 'foo' an die Instanz der Funktion gebunden, und wir können sie hier direkt lesen, indem wir auf das Attribut cell_contents der ersten (und nur) Zelle im Tupel des Attributs __closure__ :

     >>> closure_instance.__closure__[0].cell_contents 'foo' 

    Abgesehen davon werden Zellobjekte in der C-API-Dokumentation beschrieben:

    "Cell" -Objekte werden verwendet, um Variablen zu implementieren, auf die von mehreren Bereichen verwiesen wird

    Und wir können den Gebrauch unserer Schließung demonstrieren und bemerken, dass 'foo' in der Funktion steckt und sich nicht ändert:

     >>> closure_instance('bar') foo bar >>> closure_instance('baz') foo baz >>> closure_instance('quux') foo quux 

    Und nichts kann es ändern:

     >>> closure_instance.__closure__ = None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: readonly attribute 

    Teilfunktionen

    Das Beispiel gibt den Verschluss als Teilfunktion, aber wenn dies unser einziges Ziel ist, kann das gleiche Ziel mit functools.partial

     >>> from __future__ import print_function # use this if you're in Python 2. >>> partial_function = functools.partial(print, 'foo') >>> partial_function('bar') foo bar >>> partial_function('baz') foo baz >>> partial_function('quux') foo quux 

    Es gibt auch kompliziertere Verschlüsse, die dem Teilfunktionsbeispiel nicht entsprechen würden, und ich werde sie weiter nachweisen, wie es die Zeit erlaubt.

    Hier ist ein Beispiel für Python3-Verschlüsse

     def closure(x): def counter(): nonlocal x x += 1 return x return counter; counter1 = closure(100); counter2 = closure(200); print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) # result i from closure 1 101 i from closure 1 102 i from closure 2 201 i from closure 1 103 i from closure 1 104 i from closure 1 105 i from closure 2 202 

    Für mich sind "Schließungen" Funktionen, die in der Lage sind, sich an die Umgebung zu erinnern, die sie geschaffen haben. Diese Funktionalität ermöglicht es Ihnen, Variablen oder Methoden innerhalb der Schließung zu verwenden, die auf andere Weise nicht in der Lage wäre, entweder zu benutzen, weil sie nicht mehr existieren oder sie aufgrund des Umfangs nicht erreichbar sind. Schauen wir uns diesen Code in Ruby an:

     def makefunction (x) def multiply (a,b) puts a*b end return lambda {|n| multiply(n,x)} # => returning a closure end func = makefunction(2) # => we capture the closure func.call(6) # => Result equal "12" 

    Es funktioniert auch wenn beide "multiplizieren" und "x" variabel nicht mehr existieren. Alles, weil die Schließung Fähigkeit zu erinnern.

    Die beste Erklärung, die ich jemals von einer Schließung gesehen habe, war, den Mechanismus zu erklären. Es ging so etwas wie folgt:

    Stellen Sie sich Ihren Programmstapel als degenerierter Baum vor, bei dem jeder Knoten nur ein Kind hat und der einzelne Blattknoten der Kontext Ihrer derzeit laufenden Prozedur ist.

    Jetzt entsperren Sie die Einschränkung, dass jeder Knoten nur ein Kind haben kann.

    Wenn Sie dies tun, können Sie ein Konstrukt ('Ausbeute') haben, das von einer Prozedur zurückkehren kann, ohne den lokalen Kontext zu verwerfen (dh es platziert es nicht aus dem Stapel, wenn Sie zurückkehren). Wenn das nächste Mal die Prozedur aufgerufen wird, nimmt der Aufruf den alten Stapel (Baum) Rahmen auf und fährt fort, wo er ausgelassen wird.

    Python ist die beste Programmiersprache der Welt.