Estou tentando implementar um encerramento em Python 2.6 e preciso acessar uma variável não local, mas parece que essa palavra-chave não está disponível em python 2.x. Como acessar variáveis não locais em fechamentos nessas versões de python?
python
closures
python-2.x
python-nonlocal
adinsa
fonte
fonte
def inner(): print d; d = {'y': 1}
. Aqui,print d
lê externod
, criando assim uma variável não locald
no escopo interno.X = 1
simplesmente vincula o nomeX
a um objeto específico (int
e ao valor1
).X = 1; Y = X
vincula dois nomes ao mesmo objeto exato. De qualquer forma, alguns objetos são mutáveis e você pode alterar seus valores.A solução a seguir é inspirada na resposta de Elias Zamaria , mas, ao contrário dessa resposta, trata corretamente várias chamadas da função externa. A "variável"
inner.y
é local para a chamada atual deouter
. Só que não é uma variável, já que isso é proibido, mas um atributo do objeto (sendo o objeto ainner
própria função ). Isso é muito feio (observe que o atributo só pode ser criado depois que ainner
função for definida), mas parece eficaz.fonte
inc()
e umdec()
retornou de exterior que aumentar e diminuir um contador compartilhada. Em seguida, você precisa decidir a qual função anexar o valor do contador atual e fazer referência a essa função a partir das outras. O que parece um tanto estranho e assimétrico. Por exemplo, emdec()
uma linha semelhanteinc.value -= 1
.Em vez de um dicionário, há menos confusão em uma classe não local . Modificando o exemplo de @ChrisB :
Então
Cada chamada outer () cria uma classe nova e distinta chamada contexto (não apenas uma nova instância). Portanto, evita o cuidado de @Nathaniel sobre o contexto compartilhado.
fonte
__slots__ = ()
e criar um objeto em vez de usar a classe, por exemplocontext.z = 3
, levantaria umAttributeError
. É possível para todas as classes, a menos que herdem de uma classe que não define slots.Acho que a chave aqui é o que você entende por "acesso". Não deve haver problema com a leitura de uma variável fora do escopo de fechamento, por exemplo,
deve funcionar como esperado (impressão 3). No entanto, substituir o valor de x não funciona, por exemplo,
continuará a imprimir 3. Pelo que entendi do PEP-3104, é isso que a palavra-chave não local deve abranger. Conforme mencionado no PEP, você pode usar uma classe para realizar a mesma coisa (meio confuso):
fonte
def ns(): pass
seguida porns.x = 3
. Não é bonito, mas é um pouco menos feio para os meus olhos.class Namespace: x = 3
?ns
é um objeto global que é o motivo pelo qual você pode fazer referêncians.x
no nível do módulo naprint
instrução no final .Há outra maneira de implementar variáveis não locais no Python 2, caso alguma das respostas aqui seja indesejável por qualquer motivo:
É redundante usar o nome da função na instrução de atribuição da variável, mas parece mais simples e limpo para mim do que colocar a variável em um dicionário. O valor é lembrado de uma chamada para outra, assim como na resposta de Chris B..
fonte
f = outer()
e depois o fizerg = outer()
,f
o contador de será reiniciado. Isso ocorre porque ambos compartilham uma únicaouter.y
variável, em vez de cada um ter sua própria variável independente. Embora este código pareça mais esteticamente agradável do que a resposta de Chris B, sua maneira parece ser a única maneira de emular o escopo léxico se você quiser chamarouter
mais de uma vez.outer.y
não envolve nada local para a chamada de função (instância)outer()
, mas atribui a um atributo do objeto de função que está vinculado ao nomeouter
em seu escopo delimitador . E, portanto, alguém poderia igualmente ter usado, por escritoouter.y
, qualquer outro nome em vez deouter
, desde que seja conhecido por estar vinculado a esse escopo. Isso está correto?outer.y
usar o nomeinner.y
(uma vez queinner
está vinculado dentro da chamadaouter()
, que é exatamente o escopo que queremos), mas colocando a inicializaçãoinner.y = 0
após a definição de interno (já que o objeto deve existir quando seu atributo é criado), mas é claro antesreturn inner
?Aqui está algo inspirado por uma sugestão de Alois Mahdal feita em um comentário sobre outra resposta :
Atualizar
Depois de olhar para trás recentemente, fiquei impressionado com o quão parecido com o decorador ele era - quando me dei conta de que implementá-lo como um iria torná-lo mais genérico e útil (embora isso possa degradar sua legibilidade em algum grau).
Observe que ambas as versões funcionam em Python 2 e 3.
fonte
Há uma verruga nas regras de escopo do python - a atribuição torna uma variável local para seu escopo de função imediatamente envolvente. Para uma variável global, você resolveria isso com a
global
palavra - chave.A solução é introduzir um objeto que é compartilhado entre os dois escopos, que contém variáveis mutáveis, mas é referenciado por meio de uma variável que não é atribuída.
Uma alternativa é o hackeamento de escopos:
Você pode ser capaz de descobrir alguns truques para obter o nome do parâmetro e
outer
, em seguida, passá-lo como varname, mas sem depender do nome,outer
você gostaria de usar um combinador Y.fonte
nonlocal
.locals()
cria um dicionário deouter()
locais no momento em queinner()
é definido, mas alterar esse dicionário não altera ov
inouter()
. Isso não funcionará mais quando você tiver mais funções internas que desejam compartilhar uma variável fechada. Digamos que uminc()
edec()
que aumentar e diminuir um contador compartilhada.nonlocal
é um recurso do Python 3.nonlocal
no Python 2 em geral . Suas ideias não cobrem o caso geral, mas apenas aquele com uma função interna. Dê uma olhada nesta essência para um exemplo. Ambas as funções internas têm seu próprio contêiner. Você precisa de um objeto mutável no escopo da função externa, como outras respostas já sugeridas.nonlocal
palavra - chave introduzida no Python 3.Outra maneira de fazer isso (embora seja muito prolixo):
fonte
Estendendo a elegante solução Martineau acima para um caso de uso prático e um pouco menos elegante, recebo:
fonte
Use uma variável global
Pessoalmente, não gosto das variáveis globais. Mas, minha proposta é baseada em https://stackoverflow.com/a/19877437/1083704 resposta
onde o usuário precisa declarar uma variável global
ranks
, toda vez que você precisa chamar oreport
. Meu aprimoramento elimina a necessidade de inicializar as variáveis de função do usuário.fonte
inner
, mas não pode atribuir a ela, mas pode modificar suas chaves e valores. Isso evita o uso de variáveis globais.