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 é x
encontrado? Algumas opções possíveis incluem a lista abaixo:
- No arquivo de origem anexo
- No espaço de nomes da classe
- Na definição da função
- Na variável de índice do loop for
- 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.
python
scope
dynamic-languages
Charles Merriam
fonte
fonte
Respostas:
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 (
def
oulambda
) e não declarados globais nessa funçãoE nclosing-function - Nomes atribuídos no escopo local de toda e qualquer função estaticamente encerrada (
def
oulambda
), de interna para externaG lobal (módulo) - Os nomes atribuídos no nível superior de um arquivo de módulo, ou executando uma
global
declaração em umdef
dentro do arquivoB uilt-in (Python) - Nomes pré-atribuído no módulo interno de nomes:
open
,range
,SyntaxError
, etcEntão, no caso de
O
for
loop não possui seu próprio espaço para nome. Em ordem LEGB, os escopos seriamdef spam
(incode3
,code4
ecode5
)def
)x
declarado globalmente no módulo (incode1
)?x
in em Python.x
nunca será encontrado emcode2
(mesmo nos casos em que você poderia esperar, veja a resposta de Antti ou aqui ).fonte
global(var_name)
está sintaticamente incorreto. A sintaxe correta seriaglobal var_name
sem parênteses. Você tem um ponto válido.>>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
y
está sendo escrito e não háglobal y
declarações - veja o comentário de @ Peter.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.
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:
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:
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.fonte
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
def
eclass
, 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
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
for
loop 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: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
def
instruçã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 dasys.modules
variável; para ter acesso a__main__
um pode usarsys.modules['__main__']
, ouimport __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 deNameError
.O escopo pode declarar que deseja explicitamente modificar a variável global (escopo do módulo), com a palavra-chave global:
Isso também é possível, mesmo que tenha sido sombreado no escopo anexo:
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:
No entanto, em python 3, o
nonlocal
vem para resgatar:A
nonlocal
documentação diz queie
nonlocal
sempre se refere ao escopo não global externo mais interno ao qual o nome foi vinculado (isto é, atribuído a, inclusive usado comofor
variável de destino, nawith
clá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 é chamadobuiltins
. 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
print
instrução No Python 2.6-2.7, você pode se apossar daprint
função Python 3 com:Na
from __future__ import print_function
verdade, ele não importa aprint
função em nenhum lugar do Python 2 - apenas desabilita as regras de análise paraprint
instrução no módulo atual, manipulandoprint
como qualquer outro identificador de variável e, assim, permitindo queprint
a função seja pesquisada nos componentes internos.fonte
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.
fonte
Um exemplo de escopo um pouco mais completo:
resultado:
fonte
method
emethod_local_ref
devem ser destacadas.method
é capaz de acessar a variável global e imprimi-la como em5. Global x
. Masmethod_local_ref
não pode porque mais tarde define uma variável local com o mesmo nome. Você pode testar isso, removendo ax = 200
linha e ver a diferençaPython resolve suas variáveis com - geralmente - três namespaces disponíveis.
Existem duas funções:
globals
elocals
que 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
x
deve 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
for
instrução (e outras instruções compostas comoif
etry
) 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. GeralmenteFoo.code2
. No entanto,self.code2
també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.variable
para escolher a instância como o espaço para nome. Você observará queself
é 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 .
fonte
Python has two namespaces available. Global and local-to-something.
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:
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 :
fonte
Em Python,
Se uma variável não puder ser encontrada no escopo atual, consulte a ordem LEGB.
fonte