Declaração não-local do Python

340

O que a nonlocalinstrução Python faz (no Python 3.0 e posterior)?

Não há documentação no site oficial do Python e help("nonlocal")também não funciona.

ooboo
fonte
4
Dê uma olhada nesta pergunta: stackoverflow.com/questions/1414304/local-functions-in-python
Matt Joiner
18
Aqui está a documentação oficial do site Python para nonlocal : docs.python.org/3/reference/… (esta documentação está disponível desde o Python 3.0, portanto, a afirmação do OP de que não existe documentação oficial estava errada)
wkschwartz
3
"There is no documentation for nonlocal".Na verdade, você pode fazer help(keyword_in_string)para documentações em Python 3 e acima
ytpillai
10
Para ser justo, os documentos oficiais meio que sugam o assunto. O exemplo da resposta selecionada deixa as coisas muito claras, tornando essa uma pergunta valiosa.
Físico louco
No tutorial oficial do Python, há uma boa explicação do conceito de escopos e namespaces com um bom exemplo .
jammon

Respostas:

471

Compare isso sem usar 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

Para isso, usando nonlocal, where inner()'s xé agora também outer()é 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

Se fôssemos usar global, ele seria vinculado xao valor "global" apropriado:

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
fonte
32
Como isso é diferente do global x?
ooboo
52
É muito semelhante - mas observe que o x externo não é global no exemplo, mas é definido na função externa.
Anon
3
@Dustin - Na verdade, se você tinha classe A com um x atributo e uma subclasse B definida nele, você remete para x de dentro B como Ax
Anon
2
O código é facilmente recuado ao definir funções internas e acaba violando a recomendação PEP8 de 79 caracteres. Alguma maneira de contornar esse problema? Uma função interna pode de alguma forma ser colocada fora da função externa? Eu sei que a pergunta parece estúpida, mas sou sincera.
22615 Tommy.carstensen
3
@ tommy.carstensen você pode passar a função como um argumento que é a beleza das funções de ordem superior. Também na programação funcional isso é chamado de composição, python não é uma linguagem FP puro, mas certamente você pode jogar com um recursos (geradores, funções de ordem superior são alguns exemplos)
superuseroi
90

Em resumo, permite atribuir valores a uma variável em um escopo externo (mas não global). Veja PEP 3104 para todos os detalhes sangrentos.

Arkady
fonte
41

Uma pesquisa no Google por "python não-local" exibiu a proposta, PEP 3104 , que descreve completamente a sintaxe e o raciocínio por trás da declaração. em resumo, funciona exatamente da mesma maneira que a globalinstrução, exceto que é usada para se referir a variáveis ​​que não são globais nem locais para a função.

Aqui está um breve exemplo do que você pode fazer com isso. O contador gerador pode ser reescrito para usar isso, para que ele se pareça mais com os idiomas de idiomas com fechamento.

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

Obviamente, você pode escrever isso como um gerador, como:

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

Mas, embora seja um python perfeitamente idiomático, parece que a primeira versão seria um pouco mais óbvia para iniciantes. Usar corretamente geradores, chamando a função retornada, é um ponto comum de confusão. A primeira versão retorna explicitamente uma função.

SingleNegationElimination
fonte
11
Eu tinha certeza de que é isso que a palavra-chave 'global' faz - cria ambientes mais elevados até atingir uma variável com esse nome. uma variável x pode ser declarada no nível do módulo, dentro de uma classe, depois separadamente em uma função dentro dessa classe e, em seguida, em uma função interna dessa função - como ela sabe a qual x se referir?
ooboo
7
o que é global é que ele só funciona para variáveis ​​globais. ele não pode ver variáveis ​​em um escopo não global abrangente.
SingleNegationElimination
Eu tentei o make_counter - no entanto, ele não retorna um gerador, mas uma função. existe uma maneira de retornar um gerador para que mais tarde eu possa iterar sobre ele?
Dejell
@Dejel: este exemplo tem como objetivo ilustrar a nonlocaldeclaração em Python; Se você quer uma sequência de números naturais, a linguagem python é realmenteitertools.count()
SingleNegationElimination
Gostaria de demonstrar a capacidade de retornar um gerador como com yield - yield na verdade retorna um gerador. Minha idéia é não usar rendimento e em vez disso talvez usar não-local ou outra solução
Dejell
15

@ooboo:

Leva o "mais próximo" do ponto de referência no código-fonte. Isso é chamado de "Escopo Lexical" e é padrão há mais de 40 anos.

Os membros da classe do Python estão realmente em um dicionário chamado __dict__e nunca serão alcançados pelo escopo lexical.

Se você não especificar, nonlocalmas sim x = 7, ele criará uma nova variável local "x". Se você especificar nonlocal, ele encontrará o "mais próximo" "x" e o atribuirá a ele. Se você especificar nonlocale não houver "x", será exibida uma mensagem de erro.

A palavra global- chave sempre me pareceu estranha, pois felizmente ignorará todos os outros "x", exceto o mais externo. Esquisito.

Danny Milosavljevic
fonte
14

help ('nonlocal') A nonlocaldeclaração


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

A nonlocalinstrução faz com que os identificadores listados se refiram a variáveis ​​previamente vinculadas no escopo anexo mais próximo. Isso é importante porque o comportamento padrão da ligação é procurar primeiro o namespace local. A instrução permite que o código encapsulado reconecte variáveis ​​fora do escopo local, além do escopo global (módulo).

Os nomes listados em uma nonlocalinstrução, diferentemente dos listados em uma globalinstrução, devem se referir a ligações preexistentes em um escopo anexo (o escopo no qual uma nova ligação deve ser criada não pode ser determinado sem ambiguidade).

Os nomes listados em uma nonlocaldeclaração não devem colidir com ligações preexistentes no escopo local.

Veja também:

PEP 3104 - Acesso a nomes em escopos externos
A especificação para a nonlocalinstrução.

Tópicos de ajuda relacionados: global, NAMESPACES

Fonte: Referência da Linguagem Python

Yossi Truzman
fonte
11
Aprenda algo novo todos os dias. Eu não tinha ideia de que você poderia usar help()palavras-chave (e agora minha mente está soprada: help()sem argumentos, fica interativo ).
Erik Youngren
6

Citação da referência do Python 3 :

A instrução não-local faz com que os identificadores listados se refiram a variáveis ​​previamente vinculadas no escopo anexo mais próximo, excluindo globais.

Como dito na referência, no caso de várias funções aninhadas, apenas a variável na função envolvente mais próxima é modificada:

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

A variável "mais próxima" pode estar a vários níveis:

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

Mas não pode ser uma variável global:

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
fonte
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
fonte
2

Meu entendimento pessoal da declaração "não-local" (e desculpem-me por ser novo no Python e na Programação em geral) é que o "não-local" é uma maneira de usar a funcionalidade Global nas funções iteradas, em vez do corpo do próprio código . Uma declaração global entre funções, se desejar.

Yossi Truzman
fonte
0

com funções internas 'não locais' (ou seja, funções internas aninhadas) podem obter permissão de leitura e ' gravação ' para essa variável específica da função pai externa . E não-local pode ser usado apenas dentro de funções internas, por exemplo:

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
fonte