Яка різниця між глобалами (), локальними () та варами ()?


146

У чому різниця між globals(), locals()і vars()? Що вони повертають? Чи корисні оновлення результатів?


Чи можна оновити будь-яке з них у python 3 та чи справді сценарій працює?
Чарлі Паркер

Відповіді:


172

Кожен із них повертає словник:

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

localsі varsмогло б використати ще одне пояснення. Якщо locals()викликається всередині функції, він оновлює дікт зі значеннями поточної локальної області змінних імен (плюс будь-які змінні закриття) на цей момент і повертає її. Кілька викликів locals()в одному кадрі стека щоразу повертають один і той же дікт - він додається до об'єкта кадру стека як його f_localsатрибут. Вміст диктанту оновлюється під час кожного locals()виклику та f_localsдоступу до кожного атрибута, але лише на таких зверненнях або атрибутах. Він не оновлюється автоматично, коли присвоюються змінні, і при призначенні записів у дикті не буде призначено відповідних локальних змінних:

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 - інші пітони можуть дозволити оновленням автоматично повертатися до локального простору імен.
  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
Частина "та будь-які призначення словнику не відображаються у фактичному локальному просторі імен", можуть бути сформульовані трохи визначеними .
Sven Marnach

Як не дивно, ви можете отримати доступ до змінних, доданих до vars()або до locals()словника, викликаних у функції, якщо ви використовуєте eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')друкується 4, коли test()виконується!
Марк Мікофскі

1
Насправді призначення dict(повертається locals()) відображається в локальному просторі імен, а зміни в локальному просторі імен трапляються в dict(у моєму пітоні). Єдине, що специфікація не гарантує такої поведінки.
скачаючи

Термін ім'я області використання виглядає більш легким для мене , ніж імен .
переобмін

1
@overexchange: import thisі в googlesite:docs.python.org namespace
Етан Фурман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.