Метка: зона видимости

globals, locals, vars, dir – инспектируем переменные

Программист на Python может узнать, какие именно переменные определенны в данный момент в интерпретаторе. Переменные можно разделить на локальные и глобальные. Глобальные определены на верхнем уровне кода снаружи функций и классов (грубо говоря без отступов слева). Локальные переменные наоборот определены внутри своих зон видимости, ограниченных классами и функциями.

Функция globals() выдает словарь глобальных переменных (ключ – имя переменной). Функция locals() возвращает словарь только локальных переменных. Пример:

x, y = 5, 10

def test():
    y, z = 33, 44
    print('globals:', globals())
    print('locals:', locals())

test()

"""Вывод:
globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': ...>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/.../vars.py', '__cached__': None, 'x': 5, 'y': 10, 'test': <function test at 0x107677280>}
locals: {'y': 33, 'z': 44}"""

Обратите внимание, что переменная y в locals() имеет другое значение, нежели чем в globals(). Это две разные переменные из разных областей, но внутри функции приоритет имеет локальная y.

Еще важно знать, что в список переменных входят не только простые переменные, которые вы определяете через знак присваивания, но и функции, классы и импортированные модули!

Через словари из locals() и globals() переменные можно не только читать, но и создавать, перезаписывать и удалять:

>>> x = 10
>>> globals()['x'] = 5
>>> x
5
>>> globals()['new_var'] = 10
>>> new_var
10
>>> del globals()['new_var']
>>> new_var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'new_var' is not defined

vars()

vars() ведет себя как locals(), если вызвана без аргумента, а если с аргументом, то она просто получает __dict__ от аргумента. Если его нет у аргумента, то будет TypeError.

class Foo:
    def __init__(self):
        self.x = 5

f = Foo()
print(vars(f))  # {'x': 5}
print(vars(f) == f.__dict__)  # True

В глобальном контексте все три функции возвращают одно и тоже – глобальные переменные. Проверьте:

print(globals())
print(locals())
print(vars())
print(globals() == locals() == vars()) # True

dir()

Без параметров dir() возвращает список имен переменных. В глобальном контексте – глобальных переменных, в локальном – список имен локальных переменных.

def test():
    x = 10
    print(dir())  # ['x']

y = 10
test()
print(dir())  # ['__annotations__', ..., '__spec__', 'test', 'y']

Все рассмотренные выше функции являются встроенными и не требуют импортов.

P. S.

В отличие он некоторых других языков в Python блоки типа for, if, while, with не создают областей видимости (scope) для переменных, то есть переменная внутри и снаружи блока будет одна и та же:

x = 1
if True:
    x = 2
print(x)  # 2

Частая ошибка – затирание внешней переменной в цикле for:

i = 10
for i in range(5):  # затирает i
    ...
print(i)  # 4

Зоны видимости отделяются только функциями, классами и модулями. Здесь все переменные x – разные:

x = 1
class Foo:
    x = 2
    def method(self):
        x = 3
        return x
print(x, Foo.x, Foo().method())  # все 3 разные

Самая широкая зона видимости называется builtin. В нее попадают все имена, известные интерпретатору в данный момент, включая вещи импортированные из других модулей.

>>> from math import pi
>>> pi, id(pi)
(3.141592653589793, 4465320624)
>>> pi = 3
>>> pi, id(pi)
(3, 4462262880)
>>> from math import pi
>>> pi, id(pi)
(3.141592653589793, 4465320624)

Казалось бы мы затерли pi, но мы затерли его лишь в глобальной области видимости. Повторно импортируя pi, мы получаем старую переменную с тем же адресом, иными словами мы достаем ее из builtin области в global.

Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈