¿Cuál es la diferencia entre globales (), locales () y vars ()?

146

¿Cuál es la diferencia entre globals(), locals()y vars()? ¿A qué regresan? ¿Son útiles las actualizaciones de los resultados?

Ethan Furman
fuente
¿Se puede actualizar alguno de estos en Python 3 y hacer que el script realmente funcione?
Charlie Parker

Respuestas:

172

Cada uno de estos devuelve un diccionario:

  • globals() siempre devuelve el diccionario del espacio de nombres del módulo
  • locals() siempre devuelve un diccionario del actual espacio de nombres
  • vars()devuelve un diccionario del espacio de nombres actual (si se llama sin argumento) o el diccionario del argumento.

localsy varspodría usar alguna explicación más. Si locals()se llama dentro de una función, actualiza un dict con los valores del espacio de nombres de la variable local actual (más cualquier variable de cierre) a partir de ese momento y lo devuelve. Varias llamadas a locals()en el mismo marco de pila devuelven el mismo dict cada vez: se adjunta al objeto de marco de pila como su f_localsatributo. El contenido del dict se actualiza en cada locals()llamada y f_localsacceso a cada atributo, pero solo en tales llamadas o accesos de atributo. No se actualiza automáticamente cuando se asignan variables, y la asignación de entradas en el dict no asignará las variables locales correspondientes:

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 da:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

El primero print(l)solo muestra una 'x'entrada, porque la asignación a locurre después de la locals()llamada. El segundo print(l), después de llamar locals()nuevamente, muestra una lentrada, a pesar de que no guardamos el valor de retorno. El tercer y cuarto prints muestran que la asignación de variables no se actualiza ly viceversa, pero después de acceder f_locals, las variables locales se copian locals()nuevamente.

Dos notas:

  1. Este comportamiento es específico de CPython: otras Pythons pueden permitir que las actualizaciones vuelvan automáticamente al espacio de nombres local.
  2. En CPython 2.x es posible hacer que esto funcione poniendo una exec "pass"línea en la función. Esto cambia la función a un modo de ejecución más antiguo y más lento que usa el locals()dict como la representación canónica de las variables locales.

Si locals()se llama fuera de una función, devuelve el diccionario real que es el espacio de nombres actual. Otros cambios en el espacio de nombres se reflejan en el diccionario, y los cambios en el diccionario se reflejan en el espacio de nombres:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

Nos da:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

Hasta ahora, todo lo que he dicho locals()también es cierto para vars()... aquí está la diferencia: vars()acepta un solo objeto como argumento, y si le da un objeto, devuelve el __dict__de ese objeto. Para un objeto típico, __dict__es donde se almacena la mayoría de sus datos de atributos. Esto incluye variables de clase y módulos globales:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

lo que nos da:

three

Tenga en cuenta que una función __dict__es su espacio de nombres de atributo, no variables locales. No tendría sentido que una función __dict__almacene variables locales, ya que la recursión y el subprocesamiento múltiple significan que puede haber múltiples llamadas a una función al mismo tiempo, cada una con sus propios locales:

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)

lo que nos da:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

Aquí, se fllama recursivamente, por lo que las llamadas internas y externas se superponen. Cada uno ve sus propias variables locales cuando llama locals(), pero ambas llamadas ven lo mismo f.__dict__y f.__dict__no tienen ninguna variable local.

Ethan Furman
fuente
44
La parte "y las asignaciones al diccionario no se reflejan en el espacio de nombres local real" podrían estar un poco definidas .
Sven Marnach
Por extraño que parezca, se puede acceder a las variables agregadas a una vars()o locals()diccionario llamado dentro de una función si se utiliza eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')imprime 4 cuando test()se ejecuta!
Mark Mikofski
1
En realidad, la asignación a dict(devuelta por locals()) se refleja en el espacio de nombres local y los cambios en el espacio de nombres local se reflejan en dict(en mi python). Lo único es que la especificación no garantiza este comportamiento.
skyking
El uso del alcance del nombre de término me parece más fácil que el espacio de nombres .
intercambio excesivo
1
@overexchange: import thisy en googlesite:docs.python.org namespace
Ethan Furman