В чем разница между globals (), locals () и vars ()?

146

В чем разница между globals(), locals()и vars()? Что они возвращают? Полезны ли обновления результатов?

Этан Фурман
источник
Можно ли обновить какой-либо из них в Python 3 и действительно ли скрипт работает?
Чарли Паркер

Ответы:

173

Каждый из них возвращает словарь:

  • globals() всегда возвращает словарь пространства имен модуля
  • locals() всегда возвращает в словарь в текущем пространстве имен
  • vars()возвращается либо в словаре текущего пространства имен (если вызывается без аргументов) , или в словаре аргумента.

localsи varsмог бы использовать еще несколько объяснений. Если locals()вызывается внутри функции, он обновляет dict значениями текущего пространства имен локальной переменной (плюс любые переменные замыкания) на тот момент и возвращает его. Несколько вызовов locals()в одном и том же фрейме стека каждый раз возвращают один и тот же dict - он присоединяется к объекту фрейма стека в качестве его f_localsатрибута. Содержимое диктовки обновляется при каждом locals()вызове и каждом f_localsдоступе к атрибуту, но только при таких вызовах или доступах к атрибутам. Он не обновляется автоматически при назначении переменных, и назначение записей в dict не будет назначать соответствующие локальные переменные:

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()

дает нам:

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

Первый print(l)показывает только 'x'запись, потому что назначение lпроисходит после locals()вызова. Второй print(l)после locals()повторного вызова показывает lзапись, хотя мы не сохранили возвращаемое значение. Третий и четвертый printэтапы показывают, что присвоение переменных не обновляется, lи наоборот, но после того, как мы f_localsполучили доступ , локальные переменные копируются locals()снова.

Две заметки:

  1. Это поведение специфично для CPython - другие Pythons могут разрешать обновлениям автоматически возвращаться в локальное пространство имен.
  2. В CPython 2.x это можно сделать, поместив exec "pass"строку в функцию. Это переключает функцию в более старый, более медленный режим выполнения, который использует locals()dict как каноническое представление локальных переменных.

Если locals()вызывается вне функции, он возвращает фактический словарь, который является текущим пространством имен. Дальнейшие изменения в пространстве имен будут отражены в словаре, и изменения в словаре будут отражены в пространстве имен:

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

дает нам:

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

Пока что все, о чем я говорил, locals()также верно для vars()... вот в чем разница: vars()принимает один объект в качестве аргумента, и если вы даете ему объект, он возвращает значение __dict__этого объекта. Для типичного объекта __dict__именно там хранится большая часть его атрибутных данных. Это включает переменные класса и глобальные переменные модуля:

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

что дает нам:

three

Обратите внимание, что функция - __dict__это пространство имен ее атрибутов, а не локальные переменные. Для функций не имеет смысла __dict__хранить локальные переменные, поскольку рекурсия и многопоточность означают, что может быть несколько вызовов функции одновременно, каждый из которых имеет свои собственные локальные переменные:

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)

что дает нам:

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

Здесь fвызывает себя рекурсивно, поэтому внутренние и внешние вызовы перекрываются. Каждый из них видит свои локальные переменные при вызове locals(), но оба вызова видят одно f.__dict__и то же , и в нем f.__dict__нет локальных переменных.

Этан Фурман
источник
4
Часть «и любые назначения словарю не отражаются в фактическом локальном пространстве имен» может быть сформулирована немного определенно .
Свен Марнач
Как ни странно, вы можете получить доступ к переменным, добавленным в vars()или locals()словарь, вызываемый внутри функции, если вы используете eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')печатает 4, когда test()выполняется!
Марк Микофски
1
На самом деле назначение dict(возвращаемое locals()) оказывается отраженным в локальном пространстве имен, а изменения в локальном пространстве имен отражаются в dict(в моем python). Единственное, что спецификация не гарантирует такое поведение.
Скайкинг
Использование области имен термина выглядит для меня более легким, чем пространство имен .
сверхобмена
1
@overexchange: import thisи в Googlesite:docs.python.org namespace
Этан Фурман