Breve descrição das regras de escopo?

472

Quais são exatamente as regras de escopo do Python?

Se eu tiver algum código:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Onde é xencontrado? Algumas opções possíveis incluem a lista abaixo:

  1. No arquivo de origem anexo
  2. No espaço de nomes da classe
  3. Na definição da função
  4. Na variável de índice do loop for
  5. Dentro do loop for

Também existe o contexto durante a execução, quando a função spamé passada em outro lugar. E talvez as funções lambda sejam um pouco diferentes?

Deve haver uma referência simples ou algoritmo em algum lugar. É um mundo confuso para programadores intermediários de Python.

Charles Merriam
fonte
2
As regras de escopo são descritas de maneira bastante concisa - mas também completamente - na documentação do Python: docs.python.org/3/reference/… .
Jefe2000 16/05/19

Respostas:

420

Na verdade, uma regra concisa para a resolução do escopo do Python, de Learning Python, 3rd. Ed. . (Essas regras são específicas para nomes de variáveis, não para atributos. Se você fizer referência a ela sem um período, essas regras serão aplicadas.)

Regra do LEGB

  • Local - Nomes atribuídos de qualquer forma a uma função ( defou lambda) e não declarados globais nessa função

  • E nclosing-function - Nomes atribuídos no escopo local de toda e qualquer função estaticamente encerrada ( defou lambda), de interna para externa

  • G lobal (módulo) - Os nomes atribuídos no nível superior de um arquivo de módulo, ou executando uma globaldeclaração em um defdentro do arquivo

  • B uilt-in (Python) - Nomes pré-atribuído no módulo interno de nomes: open, range, SyntaxError, etc

Então, no caso de

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

O forloop não possui seu próprio espaço para nome. Em ordem LEGB, os escopos seriam

  • L: no local def spam(in code3, code4e code5)
  • E: Quaisquer funções anexas (se o exemplo inteiro estiver em outro def)
  • G: Houve algum xdeclarado globalmente no módulo (in code1)?
  • B: Qualquer built- xin em Python.

xnunca será encontrado em code2(mesmo nos casos em que você poderia esperar, veja a resposta de Antti ou aqui ).

Rizwan Kassim
fonte
45
Como uma ressalva ao acesso Global - a leitura de uma variável global pode acontecer sem declaração explícita, mas a gravação sem declarar global (var_name) criará uma nova instância local.
Peter Gibson
12
Na verdade, @ Peter, global(var_name)está sintaticamente incorreto. A sintaxe correta seria global var_namesem parênteses. Você tem um ponto válido.
martineau
Se assim for, então porque não é do foo "y" variável visível para "bar" abaixo: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer
3
@ Jonathan: Porque cada um yestá sendo escrito e não há global ydeclarações - veja o comentário de @ Peter.
22613 martineau
@LakshmanPrasad Ele se encaixa em "E", mas tem um comportamento especial que vale a pena mencionar: é uma variável de classe, por isso é um "global" entre seus objetos. Atribuir a ele vai levar a imprevisíveis e difíceis de depurar problemas se você não sabe o que está fazendo.
Ctrl-C
157

Essencialmente, a única coisa no Python que introduz um novo escopo é uma definição de função. As classes são um caso um pouco especial, pois qualquer coisa definida diretamente no corpo é colocada no espaço de nomes da classe, mas elas não são acessíveis diretamente nos métodos (ou classes aninhadas) que elas contêm.

No seu exemplo, existem apenas três escopos nos quais x será pesquisado:

  • escopo do spam - contendo tudo definido no código3 e no código5 (assim como no código4, sua variável de loop)

  • O escopo global - contendo tudo o que é definido no código1, bem como o Foo (e o que muda depois dele)

  • O espaço para nome interno. Um caso especial - ele contém as várias funções e tipos internos do Python, como len () e str (). Geralmente, isso não deve ser modificado por nenhum código de usuário; portanto, espere que ele contenha as funções padrão e nada mais.

Mais escopos aparecem apenas quando você introduz uma função aninhada (ou lambda) na imagem. No entanto, eles se comportarão da maneira que você esperaria. A função aninhada pode acessar tudo no escopo local, bem como qualquer coisa no escopo da função envolvente. por exemplo.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Restrições:

Variáveis ​​em escopos diferentes das variáveis ​​da função local podem ser acessadas, mas não podem ser recuperadas para novos parâmetros sem sintaxe adicional. Em vez disso, a atribuição criará uma nova variável local em vez de afetar a variável no escopo pai. Por exemplo:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Para realmente modificar as ligações de variáveis ​​globais de dentro de um escopo de função, você precisa especificar que a variável é global com a palavra-chave global. Por exemplo:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Atualmente, não há como fazer o mesmo para variáveis ​​nos escopos de função , mas o Python 3 introduz uma nova palavra-chave, " nonlocal" que atuará de maneira semelhante à global, mas para escopos de função aninhados.

Brian
fonte
111

Como não havia uma resposta completa sobre o tempo do Python3, fiz uma resposta aqui. A maior parte do que é descrito aqui está detalhada na 4.2.2 Resolução de nomes da documentação do Python 3.

Conforme fornecido em outras respostas, existem 4 escopos básicos, o LEGB, para Local, Enclosing, Global e Builtin. Além desses, há um escopo especial, o corpo da classe , que não compreende um escopo anexo para os métodos definidos na classe; quaisquer atribuições no corpo da classe fazem com que a variável a partir daí seja vinculada no corpo da classe.

Especialmente, nenhuma declaração de bloco, além de defe class, cria um escopo variável. No Python 2, a compreensão da lista não cria um escopo de variável, no entanto, no Python 3, a variável de loop nas compreensões da lista é criada em um novo escopo.

Demonstrar as peculiaridades do corpo de classe

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Assim, diferentemente do corpo da função, você pode reatribuir a variável ao mesmo nome no corpo da classe, para obter uma variável de classe com o mesmo nome; outras pesquisas sobre esse nome são resolvidas para a variável de classe.


Uma das maiores surpresas para muitos iniciantes no Python é que um forloop não cria um escopo variável. No Python 2, as compreensões da lista também não criam um escopo (enquanto os geradores e as compreensões de ditado criam!) Em vez disso, elas vazam o valor na função ou no escopo global:

>>> [ i for i in range(5) ]
>>> i
4

As compreensões podem ser usadas como uma maneira astuta (ou péssima, se você desejar) de criar variáveis ​​modificáveis ​​nas expressões lambda no Python 2 - uma expressão lambda cria um escopo de variável, como a definstrução faria, mas no lambda nenhuma instrução é permitida. A atribuição sendo uma instrução em Python significa que nenhuma atribuição de variável no lambda é permitida, mas a compreensão da lista é uma expressão ...

Esse comportamento foi corrigido no Python 3 - nenhuma expressão de compreensão ou geradores vazam variáveis.


O global realmente significa o escopo do módulo; o principal módulo python é o __main__; todos os módulos importados são acessíveis através da sys.modulesvariável; para ter acesso a __main__um pode usar sys.modules['__main__'], ou import __main__; é perfeitamente aceitável acessar e atribuir atributos lá; eles aparecerão como variáveis ​​no escopo global do módulo principal.


Se um nome for designado no escopo atual (exceto no escopo da classe), ele será considerado pertencente a esse escopo; caso contrário, será considerado pertencente a qualquer escopo que atribua à variável (ele pode não ser atribuído ainda, ou de modo algum) ou, finalmente, o escopo global. Se a variável for considerada local, mas ainda não estiver definida ou tiver sido excluída, a leitura do valor da variável resultará em UnboundLocalError, que é uma subclasse de NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

O escopo pode declarar que deseja explicitamente modificar a variável global (escopo do módulo), com a palavra-chave global:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Isso também é possível, mesmo que tenha sido sombreado no escopo anexo:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

No python 2, não há maneira fácil de modificar o valor no escopo anexo; geralmente isso é simulado por ter um valor mutável, como uma lista com comprimento 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

No entanto, em python 3, o nonlocalvem para resgatar:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

A nonlocaldocumentação diz que

Os nomes listados em uma declaração não-local, diferentemente dos listados em uma declaração global, 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).

ie nonlocalsempre se refere ao escopo não global externo mais interno ao qual o nome foi vinculado (isto é, atribuído a, inclusive usado como forvariável de destino, na withcláusula ou como parâmetro de função).


Qualquer variável que não seja considerada local para o escopo atual, ou qualquer escopo anexo, é uma variável global. Um nome global é pesquisado no dicionário global do módulo; se não encontrado, o global é pesquisado a partir do módulo builtins; o nome do módulo foi alterado de python 2 para python 3; no python 2 era __builtin__e no python 3 agora é chamado builtins. Se você atribuir a um atributo do módulo builtins, ele será visível a seguir para qualquer módulo como uma variável global legível, a menos que esse módulo os sombreie com sua própria variável global com o mesmo nome.


A leitura do módulo interno também pode ser útil; suponha que você queira a função de impressão no estilo python 3 em algumas partes do arquivo, mas outras partes do arquivo ainda usem a printinstrução No Python 2.6-2.7, você pode se apossar da printfunção Python 3 com:

import __builtin__

print3 = __builtin__.__dict__['print']

Na from __future__ import print_functionverdade, ele não importa a printfunção em nenhum lugar do Python 2 - apenas desabilita as regras de análise para printinstrução no módulo atual, manipulando printcomo qualquer outro identificador de variável e, assim, permitindo que printa função seja pesquisada nos componentes internos.

Antti Haapala
fonte
23

As regras de escopo do Python 2.x já foram descritas em outras respostas. A única coisa que gostaria de acrescentar é que, no Python 3.0, também existe o conceito de um escopo não local (indicado pela palavra-chave 'nonlocal'). Isso permite que você acesse escopos externos diretamente e abre a capacidade de fazer alguns truques, incluindo fechamentos lexicais (sem hacks feios envolvendo objetos mutáveis).

EDIT: Aqui está o PEP com mais informações sobre isso.

Jeremy Cantrell
fonte
23

Um exemplo de escopo um pouco mais completo:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

resultado:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
brianray
fonte
6
Esta é uma ótima resposta. No entanto, acho que as diferenças entre methode method_local_refdevem ser destacadas. methodé capaz de acessar a variável global e imprimi-la como em 5. Global x. Mas method_local_refnão pode porque mais tarde define uma variável local com o mesmo nome. Você pode testar isso, removendo a x = 200linha e ver a diferença
Kiril
@brianray: E o z?
Malik A. Rumi
@kiril Adicionei uma observação sobre isso
brianray 15/11
@ MalikA.Rumi I removido z como não era interessante
brianray
Surpreendentemente, esta é a única explicação clara dos escopos do Python, que eu pude encontrar em todos os SO. Simplesmente usando um exemplo muito básico. Obrigado!
Not2qubit 7/10
13

Python resolve suas variáveis ​​com - geralmente - três namespaces disponíveis.

A qualquer momento durante a execução, há pelo menos três escopos aninhados cujos espaços para nome são diretamente acessíveis: o escopo mais interno, pesquisado primeiro, contém os nomes locais; os espaços para nome de quaisquer funções anexas, pesquisadas a partir do escopo anexo mais próximo; o escopo do meio, pesquisado a seguir, contém os nomes globais do módulo atual; e o escopo mais externo (pesquisado por último) é o espaço para nome que contém nomes internos.

Existem duas funções: globalse localsque mostram o conteúdo de dois desses namespaces.

Os espaços para nome são criados por pacotes, módulos, classes, construção de objetos e funções. Não há outros tipos de namespaces.

Nesse caso, a chamada para uma função denominada xdeve ser resolvida no espaço de nome local ou no espaço de nome global.

Local neste caso, é o corpo da função método Foo.spam.

Global é - bem - global.

A regra é procurar os espaços locais aninhados criados por funções de método (e definições de função aninhadas) e, em seguida, procurar global. É isso aí.

Não há outros escopos. A forinstrução (e outras instruções compostas como ife try) não criam novos escopos aninhados. Somente definições (pacotes, módulos, funções, classes e instâncias de objetos).

Dentro de uma definição de classe, os nomes fazem parte do espaço para nome da classe. code2, por exemplo, deve ser qualificado pelo nome da classe. Geralmente Foo.code2. No entanto, self.code2também funcionará porque os objetos Python consideram a classe que contém como um substituto.

Um objeto (uma instância de uma classe) possui variáveis ​​de instância. Esses nomes estão no espaço de nome do objeto. Eles devem ser qualificados pelo objeto. ( variable.instance.)

De dentro de um método de classe, você tem locais e globais. Você diz self.variablepara escolher a instância como o espaço para nome. Você observará que selfé um argumento para cada função de membro da classe, fazendo parte do espaço para nome local.

Consulte Regras do escopo do Python , Escopo do Python , Escopo variável .

S.Lott
fonte
5
Isso está desatualizado. Desde 2.1 (7 anos atrás), existem mais de dois escopos, pois as funções aninhadas introduzem novos escopos, portanto, uma função dentro de uma função terá acesso ao seu escopo local, ao escopo das funções anexas e ao escopo global (também embutido).
Brian
Sinto muito, este não é mais o caso. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim
9

Onde x é encontrado?

x não foi encontrado porque você não o definiu. :-) Pode ser encontrado no código1 (global) ou no código3 (local) se você o colocar lá.

code2 (membros da classe) não é visível para codificar dentro de métodos da mesma classe - você normalmente os acessaria usando self. code4 / code5 (loops) vivem no mesmo escopo que code3, portanto, se você escrevesse x lá, alteraria a instância x definida no código3, sem criar um novo x.

O Python tem escopo estatístico; portanto, se você passar 'spam' para outra função, o spam ainda terá acesso às globais no módulo de origem (definido no código1) e a qualquer outro escopo que contenha (veja abaixo). Os membros do code2 seriam novamente acessados ​​através de si mesmos.

lambda não é diferente de def. Se você tem um lambda usado dentro de uma função, é o mesmo que definir uma função aninhada. No Python 2.2 em diante, escopos aninhados estão disponíveis. Nesse caso, você pode vincular x em qualquer nível de aninhamento de funções e o Python selecionará a instância mais interna:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

O fun3 vê a instância x do escopo que contém o mais próximo, que é o escopo da função associada ao fun2. Mas as outras x instâncias, definidas em fun1 e globalmente, não são afetadas.

Antes do nested_scopes - no Python pré-2.1 e no 2.1, a menos que você solicite especificamente o recurso usando uma importação de futuro - os escopos de fun1 e fun2 não são visíveis para o fun3, então a resposta de S.Lott é válida e você obteria o x global :

0 0
bobince
fonte
1

Em Python,

qualquer variável a que seja atribuído um valor é local para o bloco em que a atribuição aparece.

Se uma variável não puder ser encontrada no escopo atual, consulte a ordem LEGB.

GraceMeng
fonte