Instruction non locale en Python


341

Que fait l' nonlocalinstruction Python (dans Python 3.0 et versions ultérieures)?

Il n'y a pas de documentation sur le site officiel de Python et help("nonlocal")ne fonctionne pas non plus.



18
Voici la documentation officielle du site Web Python pour les non-locaux: docs.python.org/3/reference/… (cette documentation est disponible depuis Python 3.0, donc l'affirmation de l'OP selon laquelle il n'y a pas de documentation officielle était juste fausse)
wkschwartz

3
"There is no documentation for nonlocal".En fait, vous pouvez le faire help(keyword_in_string)pour les documentations en Python 3 et supérieur
ytpillai

10
Pour être honnête, les documents officiels ne plaisantent pas sur le sujet. L'exemple de la réponse sélectionnée rend les choses très claires, ce qui en fait une question précieuse.
Mad Physicist

Dans le tutoriel officiel Python, il y a une bonne explication du concept des étendues et des espaces de noms avec un bel exemple .
jammon

Réponses:


473

Comparez cela, sans utiliser 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

Pour cela, en utilisant nonlocal, où inner()« s xest maintenant 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

Si nous devions l'utiliser global, il se lierait xà la valeur correctement "globale":

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

32
En quoi est-ce différent de x global?
ooboo

52
C'est très similaire - mais notez que le x externe n'est pas global dans l'exemple mais est plutôt défini dans la fonction externe.
Anon

3
@Dustin - En fait, si vous aviez une classe A avec un attribut x et une sous-classe B définis, vous vous référeriez à x depuis B comme Axe
Anon

2
Le code est facilement fortement indenté lors de la définition des fonctions internes et finit par violer la recommandation PEP8 de 79 caractères. Une façon de contourner ce problème? Une fonction intérieure peut-elle être placée d'une manière ou d'une autre en dehors de la fonction extérieure? Je sais que la question semble stupide, mais je suis sérieux.
tommy.carstensen

3
@ tommy.carstensen vous pourriez passer la fonction comme un argument qui est la beauté des fonctions d'ordre supérieur. Aussi dans la programmation fonctionnelle, cela s'appelle la composition, python n'est pas un langage FP pur mais vous pouvez certainement jouer avec des fonctionnalités (générateurs, fonctions d'ordre supérieur sont quelques exemples)
superuseroi

90

En bref, il vous permet d'affecter des valeurs à une variable dans une étendue externe (mais non globale). Voir PEP 3104 pour tous les détails sanglants.


41

Une recherche google pour "python non local" a abouti à la proposition, PEP 3104 , qui décrit pleinement la syntaxe et le raisonnement derrière la déclaration. en bref, il fonctionne exactement de la même manière que l' globalinstruction, sauf qu'il est utilisé pour faire référence à des variables qui ne sont ni globales ni locales à la fonction.

Voici un bref exemple de ce que vous pouvez faire avec cela. Le générateur de compteur peut être réécrit pour l'utiliser afin qu'il ressemble davantage aux idiomes des langues avec fermetures.

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

Évidemment, vous pouvez écrire ceci en tant que générateur, comme:

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

Mais alors que c'est du python parfaitement idiomatique, il semble que la première version serait un peu plus évidente pour les débutants. Utiliser correctement les générateurs, en appelant la fonction retournée, est un point commun de confusion. La première version renvoie explicitement une fonction.


1
J'étais sûr que c'est ce que fait le mot-clé «global» - fonctionne dans des environnements plus élevés jusqu'à ce qu'il atteigne une variable avec ce nom. une variable x pourrait être déclarée au niveau du module, à l'intérieur d'une classe, puis séparément dans une fonction à l'intérieur de cette classe puis dans une fonction interne de cette fonction - comment sait-elle à quel x se référer?
ooboo

7
ce qui est global, c'est qu'il ne fonctionne que pour les variables globales. il ne peut pas voir les variables dans une portée non globale englobante.
SingleNegationElimination

J'ai essayé le make_counter - mais il ne retourne pas un générateur mais une fonction. y a-t-il un moyen de retourner un générateur pour que plus tard je puisse le parcourir?
Dejell

@Dejel: cet exemple est destiné à illustrer l' nonlocalinstruction en Python; Si vous voulez une séquence de nombres naturels, l'idiome python est en faititertools.count()
SingleNegationElimination

Je voudrais démontrer la possibilité de retourner un générateur comme avec yield - yield retourne en fait un générateur. Mon idée n'est pas d'utiliser le rendement et d'utiliser plutôt une solution non locale ou autre
Dejell

15

@ooboo:

Il prend celui "le plus proche" du point de référence dans le code source. C'est ce qu'on appelle le "Lexical Scoping" et il est standard depuis plus de 40 ans maintenant.

Les membres de classe de Python sont vraiment dans un dictionnaire appelé __dict__et ne seront jamais atteints par la portée lexicale.

Si vous ne spécifiez pas nonlocalmais que vous le faites x = 7, cela créera une nouvelle variable locale "x". Si vous le spécifiez nonlocal, il trouvera le "x" le plus "proche" et l'affectera à cela. Si vous spécifiez nonlocalet qu'il n'y a pas de "x", cela vous donnera un message d'erreur.

Le mot-clé globalm'a toujours paru étrange car il ignorera volontiers tous les autres "x" à l'exception du plus externe. Bizarre.


14

help ('nonlocal') La nonlocaldéclaration


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

L' nonlocalinstruction oblige les identificateurs répertoriés à faire référence aux variables précédemment liées dans la portée englobante la plus proche. Ceci est important car le comportement par défaut de la liaison consiste à rechercher d'abord l'espace de noms local. L'instruction permet au code encapsulé de relier des variables en dehors de la portée locale en plus de la portée globale (module).

Les noms répertoriés dans une nonlocalinstruction, contrairement à ceux répertoriés dans une globalinstruction, doivent faire référence à des liaisons préexistantes dans une étendue englobante (l'étendue dans laquelle une nouvelle liaison doit être créée ne peut pas être déterminée sans ambiguïté).

Les noms répertoriés dans une nonlocalinstruction ne doivent pas entrer en collision avec des liaisons préexistantes dans la portée locale.

Voir également:

PEP 3104 - Accès aux noms dans les étendues externes
La spécification de l' nonlocalinstruction.

Rubriques d'aide connexes: global, NAMESPACES

Source: Référence du langage Python


11
Apprenez quelque chose de nouveau tous les jours. Je n'avais aucune idée que vous pourriez utiliser help()sur des mots clés (et maintenant mon esprit est époustouflé: help()sans argument, il devient interactif ).
Erik Youngren

6

Citation de la référence Python 3 :

L'instruction non locale fait que les identificateurs répertoriés se réfèrent aux variables précédemment liées dans la portée englobante la plus proche, à l'exclusion des globaux.

Comme indiqué dans la référence, dans le cas de plusieurs fonctions imbriquées, seule la variable dans la fonction englobante la plus proche est modifiée:

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

La variable "la plus proche" peut être à plusieurs niveaux:

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

Mais il ne peut pas s'agir d'une variable globale:

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

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)

2

Ma compréhension personnelle de la déclaration "non locale" (et excusez-moi car je suis nouveau sur Python et la programmation en général) est que le "non local" est un moyen d'utiliser la fonctionnalité globale dans les fonctions itérées plutôt que le corps du code lui-même . Une instruction globale entre les fonctions si vous le souhaitez.


0

avec les fonctions internes «non locales» (c'est-à-dire les fonctions internes imbriquées) peuvent obtenir la permission de lecture et « écriture » pour cette variable spécifique de la fonction parent externe . Et non local ne peut être utilisé qu'à l'intérieur de fonctions internes, par exemple:

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)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.