wake-up-neo.com

Wie funktionieren verschachtelte Funktionen in Python?

def maker(n):
    def action(x):
        return x ** n
    return action

f = maker(2)
print(f)
print(f(3))
print(f(4))

g = maker(3)
print(g(3))

print(f(3)) # still remembers 2

Warum speichert die verschachtelte Funktion den ersten Wert 2, Obwohl maker() zurückgegeben und beendet wurde, bis action() aufgerufen wird?

54
3zzy

Sie können sehen, dass alle Variablen, die aus der übergeordneten Funktion stammen, durch ihren tatsächlichen Wert in der untergeordneten Funktion ersetzt werden. Auf diese Weise muss der Bereich der übergeordneten Funktion nicht nachverfolgt werden, damit die untergeordnete Funktion ordnungsgemäß ausgeführt wird.

Sehen Sie es als "eine Funktion dynamisch erstellen".

def maker(n):
  def action(x):
    return x ** n
  return action

f = maker(2)
--> def action(x):
-->   return x ** 2

Dies ist ein grundlegendes Verhalten in Python. Dasselbe gilt für mehrere Zuweisungen.

a = 1
b = 2
a, b = b, a

Python liest dies als

a, b = 2, 1

Grundsätzlich werden die Werte eingefügt, bevor etwas mit ihnen gemacht wird.

30
Tor Valamo

Sie erstellen im Grunde ein Closure .

In der Informatik ist ein Abschluss eine erstklassige Funktion mit freien Variablen, die in der lexikalischen Umgebung gebunden sind. Eine solche Funktion soll über ihre freien Variablen "geschlossen" werden.

Verwandte Lektüre: Abschlüsse: Warum sind sie so nützlich?

Ein Abschluss ist einfach eine bequemere Möglichkeit, einer Funktion Zugriff auf den lokalen Status zu gewähren.

Von http://docs.python.org/reference/compound_stmts.html :

Hinweis für Programmierer: Funktionen sind erstklassige Objekte. Ein 'def'-Formular, das in einer Funktionsdefinition ausgeführt wird, definiert eine lokale Funktion, die zurückgegeben oder weitergegeben werden kann. Freie Variablen, die in der verschachtelten Funktion verwendet werden, können auf die lokalen Variablen der Funktion zugreifen, die die def enthält. Siehe Abschnitt Benennung und Bindung für Details.

38
miku

Sie definieren ZWEI Funktionen. Wenn du anrufst

f = maker(2)

sie definieren eine Funktion, die die doppelte Zahl zurückgibt

f(2) --> 4
f(3) --> 6

Anschließend definieren Sie EINE ANDERE VERSCHIEDENE FUNKTION

g = maker(3)

das gibt das Dreifache der Zahl zurück

g(3) ---> 9

Es handelt sich jedoch um ZWEI verschiedene Funktionen. Es handelt sich nicht um dieselbe referenzierte Funktion, sondern um eine eigenständige. Auch wenn der Gültigkeitsbereich innerhalb der Funktion 'maker' gleich heißt, ist dies nicht dieselbe Funktion, jedes Mal, wenn Sie maker() aufrufen, definieren Sie eine andere Funktion. Es ist wie eine lokale Variable, die jedes Mal, wenn Sie die Funktion aufrufen, denselben Namen erhält, jedoch unterschiedliche Werte enthalten kann. In diesem Fall enthält die Variable 'action' eine Funktion (die unterschiedlich sein kann)

14
Khelben

Das nennt man " Abschluß ". Einfach ausgedrückt, für die meisten, wenn nicht alle Programmiersprachen, die Funktionen als erstklassiges Objekt behandeln, werden alle Variablen, die in einem Funktionsobjekt verwendet werden, eingeschlossen (d. H. Gespeichert), solange die Funktion noch aktiv ist. Es ist ein leistungsfähiges Konzept, wenn Sie wissen, wie man es nutzt.

In Ihrem Beispiel verwendet die geschachtelte Funktion action die Variable n, sodass diese Variable abgeschlossen und für spätere Funktionsaufrufe gespeichert wird.

9
Lukman

Schauen wir uns drei häufige Gründe für das Schreiben innerer Funktionen an.

1. Verschlüsse und Werksfunktionen

Der Wert im umschließenden Gültigkeitsbereich bleibt erhalten, auch wenn die Variable den Gültigkeitsbereich verlässt oder die Funktion selbst aus dem aktuellen Namespace entfernt wird.

def print_msg(msg):
    """This is the outer enclosing function"""

    def printer():
        """This is the nested function"""
        print(msg)

    return printer  # this got changed

Versuchen wir nun, diese Funktion aufzurufen.

>>> another = print_msg("Hello")
>>> another()
Hello

Das ist unüblich. Die Funktion print_msg() wurde mit dem String "Hello" Aufgerufen und die zurückgegebene Funktion an den Namen another gebunden. Beim Aufruf von another() wurde die Nachricht noch gespeichert, obwohl wir die Funktion print_msg() bereits ausgeführt hatten. Diese Technik, mit der einige Daten ("Hello") An den Code angehängt werden, wird in Python als Closure bezeichnet.

Wozu sind Verschlüsse gut? Closures können die Verwendung globaler Werte vermeiden und bieten eine Form des Versteckens von Daten. Es kann auch eine objektorientierte Lösung für das Problem liefern. Wenn in einer Klasse nur wenige Methoden (in den meisten Fällen eine Methode) implementiert werden müssen, können Verschlüsse alternative und elegantere Lösungen bieten. Referenz

2. Kapselung:

Allgemeines Konzept der Kapselung ist, die innere Welt vor der äußeren zu verbergen und zu schützen. Hier kann auf innere Funktionen nur innerhalb der äußeren zugegriffen werden und sie sind vor allem geschützt, was außerhalb der Funktion geschieht.

3. Keepin 'it DRY

Möglicherweise haben Sie eine Riesenfunktion, die an zahlreichen Stellen denselben Codeabschnitt ausführt. Beispielsweise könnten Sie eine Funktion schreiben, die eine Datei verarbeitet, und Sie möchten entweder ein geöffnetes Dateiobjekt oder einen Dateinamen akzeptieren:

def process(file_name):
    def do_stuff(file_process):
        for line in file_process:
            print(line)
    if isinstance(file_name, str):
        with open(file_name, 'r') as f:
            do_stuff(f)
    else:
        do_stuff(file_name)

Weitere können Sie beziehen this Blog.

3
sujit tiwari

Denn zum Zeitpunkt der Erstellung der Funktion war n2. Ihre Funktion lautet also:

def action(x):
    return x ** 2

Wenn Sie f (3) aufrufen, wird x auf 3 Gesetzt, sodass Ihre Funktion 3 ** 2 Zurückgibt.

2
James Polley

Eine Verwendung besteht darin, eine Funktion zurückzugeben, die einen Parameter verwaltet.

def outer_closure(a):
    #  parm = a               <- saving a here isn't needed
    def inner_closure():
        #return parm
        return a              # <- a is remembered 
    return inner_closure

# set parm to 5 and return address of inner_closure function
x5 = outer_closure(5)
x5()
>5

x6 = outer_closure(6)
x6()
>6

# x5 inner closure function instance of parm persists 
x5()
>5
1
kztd

Die Leute antworteten richtig auf die Schließung, das heißt: Der gültige Wert für "n" inside action ist der letzte Wert, den es hatte, als "maker" aufgerufen wurde.

Eine einfache Möglichkeit, dies zu überwinden, besteht darin, Ihren Freevar (n) zu einer Variablen innerhalb der "action" -Funktion zu machen, die beim Ausführen eine Kopie von "n" erhält:

Am einfachsten ist es, "n" als Parameter festzulegen, dessen Standardwert zum Zeitpunkt der Erstellung "n" ist. Dieser Wert für "n" bleibt fest, da Standardparameter für eine Funktion in einem Tupel gespeichert sind, das ein Attribut der Funktion selbst ist (action.func_defaults in diesem Fall):

def maker(n):
    def action(x, k=n):
        return x ** k
    return action

Verwendung:

f = maker(2) # f is action(x, k=2)
f(3)   # returns 3^2 = 9
f(3,3) # returns 3^3 = 27
1
jsbueno

Wenn Sie eine Funktion mit dem Schlüsselwort def erstellen, tun Sie genau das: Sie erstellen ein neues Funktionsobjekt und weisen es einer Variablen zu. In dem Code, den Sie angegeben haben, weisen Sie dieses neue Funktionsobjekt einer lokalen Variablen namens action zu.

Wenn Sie es ein zweites Mal aufrufen, erstellen Sie ein zweites Funktionsobjekt. Also zeigt f auf das erste Funktionsobjekt (Quadrat-der-Wert) und g zeigt auf das zweite Funktionsobjekt (Würfel-der-Wert). Wenn Python "f (3)" sieht, bedeutet dies "das Funktionsobjekt ausführen, das als Variable f gekennzeichnet ist, und ihm den Wert 3" übergeben. F und g und verschiedene Funktionsobjekte und so weiter verschiedene Werte zurückgeben.

0
DavidG