Nichtlokale Python-Anweisung

340

Was macht die Python- nonlocalAnweisung (in Python 3.0 und höher)?

Es gibt keine Dokumentation auf der offiziellen Python-Website und help("nonlocal")funktioniert auch nicht.

ooboo
quelle
4
Schauen Sie sich diese Frage an: stackoverflow.com/questions/1414304/local-functions-in-python
Matt Joiner
18
Hier ist die offizielle Dokumentation der Python-Website für Nicht-Lokale : docs.python.org/3/reference/… (diese Dokumentation ist seit Python 3.0 verfügbar, daher war die Behauptung des OP, dass es keine offizielle Dokumentation gibt, einfach falsch)
wkschwartz
3
"There is no documentation for nonlocal".Eigentlich können Sie help(keyword_in_string)für Dokumentationen in Python 3 und höher
tun
10
Um fair zu sein, saugen die offiziellen Dokumente an dem Thema. Das Beispiel der ausgewählten Antwort macht die Dinge sehr deutlich und macht dies zu einer wertvollen Frage.
Verrückter Physiker
Im offiziellen Python-Tutorial finden Sie eine gute Erklärung des Konzepts von Bereichen und Namespaces anhand eines schönen Beispiels .
Jammon

Antworten:

471

Vergleichen Sie dies, ohne zu verwenden nonlocal:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0

Dazu verwenden nonlocal, wo inner()'s xist jetzt auch outer()' s x:

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0

Wenn wir verwenden würden global, würde es xan den richtigen "globalen" Wert binden :

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2
Anon
quelle
32
Wie unterscheidet sich das von globalem x?
Ooboo
52
Es ist sehr ähnlich - aber beachten Sie, dass das äußere x im Beispiel nicht global ist, sondern in der äußeren Funktion definiert wird.
Anon
3
@Dustin - Wenn Sie Klasse A mit einem Attribut x und einer darin definierten Unterklasse B hätten, würden Sie x aus B heraus als Ax
Anon
2
Der Code wird beim Definieren innerer Funktionen leicht stark eingerückt und verstößt gegen die PEP8-Empfehlung mit 79 Zeichen. Wie kann man dieses Problem umgehen? Kann eine innere Funktion irgendwie außerhalb der äußeren Funktion platziert werden? Ich weiß, dass die Frage dumm klingt, aber ich meine es ernst.
Tommy.carstensen
3
@ tommy.carstensen Sie könnten die Funktion als Argument übergeben, das das Schöne an Funktionen höherer Ordnung ist. Auch in der funktionalen Programmierung wird dies als Komposition bezeichnet. Python ist keine reine FP-Sprache, aber Sie können sicherlich mit Funktionen spielen (Generatoren, Funktionen höherer Ordnung sind einige Beispiele)
Superuseroi
90

Kurz gesagt, Sie können einer Variablen in einem äußeren (aber nicht globalen) Bereich Werte zuweisen. Siehe PEP 3104 für alle wichtigen Details.

Arkady
quelle
41

Eine Google-Suche nach "python nonlocal" ergab den Vorschlag PEP 3104 , der die Syntax und die Argumentation hinter der Aussage vollständig beschreibt. Kurz gesagt, es funktioniert genauso wie die globalAnweisung, außer dass es verwendet wird, um auf Variablen zu verweisen, die für die Funktion weder global noch lokal sind.

Hier ist ein kurzes Beispiel dafür, was Sie damit machen können. Der Zählergenerator kann so umgeschrieben werden, dass er eher den Redewendungen von Sprachen mit Verschlüssen ähnelt.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

Natürlich könnten Sie dies als Generator schreiben, wie:

def counter_generator():
    count = 0
    while True:
        count += 1
        yield count

Obwohl dies eine vollkommen idiomatische Python ist, scheint die erste Version für Anfänger etwas offensichtlicher zu sein. Die ordnungsgemäße Verwendung von Generatoren durch Aufrufen der zurückgegebenen Funktion ist ein häufiger Verwirrungspunkt. Die erste Version gibt explizit eine Funktion zurück.

SingleNegationElimination
quelle
1
Ich war mir sicher, dass das Schlüsselwort 'global' genau das tut - es bearbeitet höhere Umgebungen, bis es eine Variable mit diesem Namen erreicht. Eine Variable x könnte auf Modulebene innerhalb einer Klasse deklariert werden, dann separat in einer Funktion innerhalb dieser Klasse und dann in einer inneren Funktion dieser Funktion - woher weiß sie, auf welches x sie sich beziehen soll?
Ooboo
7
Die Sache mit global ist, dass es nur für globale Variablen funktioniert. Es können keine Variablen in einem umschließenden, nicht globalen Bereich angezeigt werden.
SingleNegationElimination
Ich habe den make_counter ausprobiert - er gibt jedoch keinen Generator, sondern eine Funktion zurück. Gibt es eine Möglichkeit, einen Generator zurückzugeben, damit ich später darüber iterieren kann?
Dejell
@Dejel: Dieses Beispiel soll die nonlocalAnweisung in Python veranschaulichen . Wenn Sie eine Folge von natürlichen Zahlen wollen, ist die Python-Sprache tatsächlichitertools.count()
SingleNegationElimination
Ich möchte die Fähigkeit demonstrieren, einen Generator wie bei Yield zurückzugeben - Yield gibt tatsächlich einen Generator zurück. Meine Idee ist nicht, Ausbeute zu verwenden, sondern vielleicht nichtlokale oder eine andere Lösung
Dejell
15

@ooboo:

Es nimmt diejenige, die dem Bezugspunkt im Quellcode "am nächsten" liegt. Dies wird als "Lexical Scoping" bezeichnet und ist seit> 40 Jahren Standard.

Pythons Klassenmitglieder befinden sich wirklich in einem Wörterbuch namens __dict__und werden niemals durch lexikalisches Scoping erreicht.

Wenn Sie nicht angeben , nonlocalaber tun x = 7, wird es eine neue lokale Variable „x“ erstellen. Wenn Sie angeben nonlocal, wird das "nächste" "x" gefunden und diesem zugewiesen. Wenn Sie angeben nonlocalund kein "x" vorhanden ist, wird eine Fehlermeldung angezeigt.

Das Schlüsselwort kam globalmir immer seltsam vor, da es alle anderen "x" mit Ausnahme des äußersten gerne ignoriert. Seltsam.

Danny Milosavljevic
quelle
14

help ('nonlocal') Die nonlocalAussage


    nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

Das nonlocal Anweisung bewirkt, dass die aufgelisteten Bezeichner auf zuvor gebundene Variablen im nächsten umschließenden Bereich verweisen. Dies ist wichtig, da das Standardverhalten für die Bindung darin besteht, zuerst den lokalen Namespace zu durchsuchen. Die Anweisung ermöglicht es gekapseltem Code, Variablen außerhalb des lokalen Bereichs neben dem globalen (Modul-) Bereich neu zu binden.

Namen, die in einer nonlocalAnweisung aufgeführt sind, im Gegensatz zu den in a global Anweisung auf bereits vorhandene Bindungen in einem umschließenden Bereich beziehen (der Bereich, in dem eine neue Bindung erstellt werden soll, kann nicht eindeutig bestimmt werden).

In einer nonlocalAnweisung aufgeführte Namen dürfen nicht mit bereits vorhandenen Bindungen im lokalen Bereich kollidieren.

Siehe auch:

PEP 3104 - Zugriff auf Namen in äußeren Bereichen
Die Spezifikation für die nonlocalAnweisung.

Verwandte Hilfethemen: global, NAMESPACES

Quelle: Python-Sprachreferenz

Yossi Truzman
quelle
11
Lerne jeden Tag etwas Neues. Ich hatte keine Ahnung, dass Sie help()Schlüsselwörter verwenden könnten (und jetzt ist mein Verstand überwältigt: help()Ohne Argumente wird interaktiv ).
Erik Youngren
6

Zitat aus der Python 3-Referenz :

Die nichtlokale Anweisung bewirkt, dass die aufgelisteten Bezeichner auf zuvor gebundene Variablen im nächsten umschließenden Bereich mit Ausnahme von Globalen verweisen.

Wie in der Referenz erwähnt, wird bei mehreren verschachtelten Funktionen nur die Variable in der nächsten umschließenden Funktion geändert:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

Die "nächste" Variable kann mehrere Ebenen entfernt sein:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

Es kann aber keine globale Variable sein:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found
Jeyekomon
quelle
3
a = 0    #1. global variable with respect to every function in program

def f():
    a = 0          #2. nonlocal with respect to function g
    def g():
        nonlocal a
        a=a+1
        print("The value of 'a' using nonlocal is ", a)
    def h():
        global a               #3. using global variable
        a=a+5
        print("The value of a using global is ", a)
    def i():
        a = 0              #4. variable separated from all others
        print("The value of 'a' inside a function is ", a)

    g()
    h()
    i()
print("The value of 'a' global before any function", a)
f()
print("The value of 'a' global after using function f ", a)
gxyd
quelle
2

Mein persönliches Verständnis der "nichtlokalen" Anweisung (und entschuldigen Sie, da ich neu in Python und Programmierung im Allgemeinen bin) ist, dass "nichtlokal" eine Möglichkeit ist, die globale Funktionalität in iterierten Funktionen und nicht im Hauptteil des Codes selbst zu verwenden . Eine globale Anweisung zwischen Funktionen, wenn Sie so wollen.

Yossi Truzman
quelle
0

mit ‚nicht - lokalen‘ inneren Funktionen (dh, verschachtelte innere Funktionen) lesen & 'erhalten kann Schreib Erlaubnis für diese bestimmte Variable der äußeren Mutterfunktion . Und nicht lokal kann nur innerhalb innerer Funktionen verwendet werden, z.

a = 10
def Outer(msg):
    a = 20
    b = 30
    def Inner():
        c = 50
        d = 60
        print("MU LCL =",locals())
        nonlocal a
        a = 100
        ans = a+c
        print("Hello from Inner",ans)       
        print("value of a Inner : ",a)
    Inner()
    print("value of a Outer : ",a)

res = Outer("Hello World")
print(res)
print("value of a Global : ",a)
NIPHIN
quelle