Qual é a diferença entre globals()
, locals()
e vars()
? O que eles retornam? As atualizações dos resultados são úteis?
Qual é a diferença entre globals()
, locals()
e vars()
? O que eles retornam? As atualizações dos resultados são úteis?
Respostas:
Cada um deles retorna um dicionário:
globals()
sempre retorna o dicionário do espaço para nome do módulolocals()
sempre retorna um dicionário do espaço para nome atualvars()
retorna um dicionário do espaço para nome atual (se chamado sem argumento) ou o dicionário do argumento.locals
e vars
poderia usar mais algumas explicações. Se locals()
for chamado dentro de uma função, ele atualiza um ditado com os valores do namespace da variável local atual (mais quaisquer variáveis de fechamento) a partir desse momento e o retorna. Várias chamadas para locals()
no mesmo quadro de pilha retornam o mesmo ditado toda vez - elas são anexadas ao objeto de quadro de pilha como seu f_locals
atributo. O conteúdo do dict é atualizado em cada locals()
chamada e f_locals
acesso a cada atributo, mas apenas nessas chamadas ou acessos a atributos. Ele não é atualizado automaticamente quando variáveis são atribuídas, e a atribuição de entradas no dict não atribuirá as variáveis locais correspondentes:
import inspect
def f():
x = 1
l = locals()
print(l)
locals()
print(l)
x = 2
print(x, l['x'])
l['x'] = 3
print(x, l['x'])
inspect.currentframe().f_locals
print(x, l['x'])
f()
nos dá:
{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2
O primeiro print(l)
mostra apenas uma 'x'
entrada, porque a atribuição l
ocorre após a locals()
chamada. O segundo print(l)
, depois de ligar locals()
novamente, mostra uma l
entrada, mesmo que não tenhamos salvado o valor de retorno. O terceiro e o quarto print
s mostram que a atribuição de variáveis não é atualizada l
e vice-versa, mas após o acesso f_locals
, as variáveis locais são copiadas locals()
novamente.
Duas notas:
exec "pass"
linha na função. Isso muda a função para um modo de execução mais antigo e lento, que usa o locals()
dict como representação canônica de variáveis locais.Se locals()
for chamado fora de uma função, ele retornará o dicionário real que é o espaço para nome atual. Outras alterações no espaço para nome são refletidas no dicionário e as alterações no dicionário são refletidas no espaço para nome:
class Test(object):
a = 'one'
b = 'two'
huh = locals()
c = 'three'
huh['d'] = 'four'
print huh
nos dá:
{
'a': 'one',
'b': 'two',
'c': 'three',
'd': 'four',
'huh': {...},
'__module__': '__main__',
}
Até agora, tudo o que eu disse locals()
também é verdadeiro vars()
... aqui está a diferença: vars()
aceita um único objeto como argumento e, se você der um objeto, ele retorna o __dict__
objeto. Para um objeto típico, __dict__
é onde a maioria dos dados de atributo é armazenada. Isso inclui variáveis de classe e globais do módulo:
class Test(object):
a = 'one'
b = 'two'
def frobber(self):
print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()
o que nos dá:
three
Observe que a função __dict__
é seu namespace de atributo, não variáveis locais. Não faria sentido para uma função __dict__
armazenar variáveis locais, já que recursão e multithreading significam que pode haver várias chamadas para uma função ao mesmo tempo, cada uma com seus próprios locais:
def f(outer):
if outer:
f(False)
print('Outer call locals:', locals())
print('f.__dict__:', f.__dict__)
else:
print('Inner call locals:', locals())
print('f.__dict__:', f.__dict__)
f.x = 3
f(True)
o que nos dá:
Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}
Aqui, se f
chama recursivamente, de modo que as chamadas internas e externas se sobrepõem. Cada um vê suas próprias variáveis locais quando chama locals()
, mas as duas chamam o mesmo f.__dict__
e f.__dict__
não possuem nenhuma variável local.
vars()
ou locals()
dicionário chamado dentro de uma função, se você usar eval()
. EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')
imprime 4 quando test()
é executado!
dict
(retornado por locals()
) é refletida no espaço para nome local e as alterações no espaço para nome local são refletidas no dict
(no meu python). A única coisa é que a especificação não garante esse comportamento.
import this
e no googlesite:docs.python.org namespace