Перейти до змісту

Простори імен: locals(), globals() і правило LEGB

locals() та globals() — вбудовані інструменти інтроспекції, які дозволяють заглянути "під капот" вашої програми. Вони показують:

  • які змінні зараз існують;
  • де саме вони існують;
  • у якому просторі імен вони зберігаються.

Обидві функції повертають словник (dict), де ключі — це імена змінних (рядки), а значення — об'єкти, на які ці імена посилаються.

Передумова

Якщо ще не знайомі з базовим поняттям області видимості (scope) і скороченою версією LEGB — прочитайте спершу Функції → 3. Область видимості. Тут — повний розбір із прикладами на кожен рівень.

Що таке простір імен (namespace)?

Namespace — це словник, який зв'язує ім'я → об'єкт у пам'яті. У Python є кілька рівнів:

  1. Built-in namespace
  2. Global namespace
  3. Local namespace

globals() — глобальний простір імен

Повертає словник змінних, функцій і класів, визначених на рівні модуля (файлу). Глобальні змінні існують весь час роботи програми та доступні з будь-якого місця модуля.

На верхньому рівні скрипта locals() і globals() — це буквально той самий словник:

print(locals() == globals())

Результат:

True

Це так, бо ми ще не всередині функції — на рівні модуля локальний і глобальний простір імен збігаються.

locals() — локальний простір імен

Повертає словник змінних, які існують у поточній функції. Локальний простір створюється при виклику функції й знищується після її завершення.

global_var = 100

def my_function(param):
    local_var = "Я всередині"
    print("--- locals() ---")
    print(locals())
    print("\n--- globals() ---")
    print(sorted(k for k in globals() if not k.startswith('__')))

my_function(42)

Результат:

--- locals() ---
{'param': 42, 'local_var': 'Я всередині'}

--- globals() ---
['global_var', 'my_function']

locals() показує лише те, що реально існує в цьому виклику функції: параметр і локальну змінну. globals() показує все, що визначено на рівні модуля — тут для наочності відфільтровано службові імена __name__, __doc__ тощо, які Python додає автоматично.

У Jupyter globals() виглядає значно більшим

У самому Jupyter/IPython globals() додатково містить десятки службових ключів (_ih, _oh, In, Out, get_ipython тощо) — це внутрішня бухгалтерія ядра ноутбука, а не щось, що ви створили. У звичайному .py-скрипті цього немає — саме тому приклади на цій сторінці показані як звичайний Python-скрипт, а не сирий вивід ноутбука.

⚠️ Важлива пастка: globals() можна змінювати, locals() — ні

globals() дозволяє реально створити змінну

globals()['new_var'] = 999
print(new_var)

Результат:

999

Це справді створює нову глобальну змінну — globals() повертає той самий словник, яким Python користується для зберігання глобальних імен, тож запис у нього одразу видно решті програми.

locals() працює лише для читання

def test():
    locals()['x'] = 5
    print("locals() dict:", locals())
    try:
        print("реальне ім'я x:", x)
    except NameError as e:
        print("NameError:", e)

test()

Результат:

locals() dict: {'x': 5}
NameError: name 'x' is not defined

Тут легко зробити неправильний висновок із того, що print(locals()) показав {'x': 5} — ніби запис спрацював. Насправді ні: усередині функції locals() повертає копію локального простору імен, і саме в цю копію запис і потрапив. Ключове підтвердження — спроба звернутися до x напряму як до імені одразу падає з NameError: реальної локальної змінної x не існує, є лише запис у тимчасовому словнику. Щоб змінити справжню локальну змінну, потрібне звичайне присвоювання: x = 5.

Практичне використання

1. Швидкий дебаг

Якщо функція працює неправильно, locals() дозволяє побачити весь стан функції в один момент:

def complex_calculation(x, y, z):
    step1 = x * y
    step2 = step1 / z
    print(locals())
    return step2

2. Контроль eval() та exec()

eval() і exec() можуть приймати власні словники globals та locals — так можна повністю контролювати, до яких імен має доступ виконуваний код:

math_formula = "x * y + 5"
my_context = {'x': 10, 'y': 2}

result = eval(math_formula, {}, my_context)
print("Результат:", result)

Результат:

Результат: 25

eval() виконує довільний код

Це приклад контролю середовища виконання, а не рекомендація виконувати недовірений ввід через eval(). Детально про небезпеку eval() і безпечні альтернативи — на сторінці eval(): від зручної функції до дірки в безпеці.

3. Форматування рядків (старий трюк)

def create_greeting(name, age, city):
    return "Привіт! Мене звати %(name)s, мені %(age)s років, я з міста %(city)s." % locals()

print(create_greeting("Ivan", 30, "Kyiv"))

Результат:

Привіт! Мене звати Ivan, мені 30 років, я з міста Kyiv.

locals() передає всі змінні функції у форматування одним рухом.

Note

У сучасному Python для форматування рядків частіше використовують f-рядки (f"...").

Коли реально використовувати

Ситуація Чи варто використовувати
Дебаг ✅ Так
Метапрограмування ✅ Так
Звичайна бізнес-логіка ❌ Ні
Створення змінних динамічно ⚠️ Обережно

Чого не варто робити:

  • ❌ будувати логіку програми через зміну globals();
  • ❌ намагатися модифікувати locals(), очікуючи, що це змінить реальну змінну;
  • ❌ використовувати для "хитрих трюків" у звичайному коді.

globals() та locals() — інструменти дослідження середовища виконання (дебаг, розуміння Python, метапрограмування, навчання), не інструменти звичайної бізнес-логіки.

Правило LEGB — як Python шукає імена

Коли Python бачить ім'я змінної, він шукає її у строгому порядку:

┌──────────────────────────────┐
│        Built-in (B)          │
│  print, len, str, int, ...   │
└──────────────▲───────────────┘
               │
┌──────────────┴───────────────┐
│        Global (G)            │
│  змінні модуля, функції      │
└──────────────▲───────────────┘
               │
┌──────────────┴───────────────┐
│     Enclosing (E)            │
│  зовнішня функція            │
└──────────────▲───────────────┘
               │
┌──────────────┴───────────────┐
│        Local (L)             │
│  поточна функція             │
└──────────────────────────────┘
Рівень Що означає Де знаходиться
L (Local) Всередині поточної функції найближчий рівень
E (Enclosing) У зовнішній функції (якщо функції вкладені) на рівень вище
G (Global) На рівні модуля (файлу) на рівень вище E
B (Built-in) Вбудовані імена Python (len, print, str) верхній рівень

Python починає з Local і рухається вгору.

1. Local

x = "global"
def test():
    x = "local"
    print("Inside function:", x)

test()
print("Outside function:", x)

Результат:

Inside function: local
Outside function: global

Python спочатку шукає в Local → знаходить "local".

2. Enclosing

def outer():
    x = "enclosing"
    def inner():
        print("Inner sees:", x)
    inner()

outer()

Результат:

Inner sees: enclosing

inner() не має власного x, тому Python піднімається до Enclosing.

3. Global

x = "global variable"
def test():
    print("Inside function:", x)

test()

Результат:

Inside function: global variable

x не знайдено в Local → немає Enclosing → береться з Global.

4. Built-in

print(len([1, 2, 3]))

Результат:

3

len не визначений у Local, Enclosing чи Global, тому Python бере його з Built-in.

Дві типові пастки

Пастка 1 — перекриття built-in

len = "oops"
print(len)

# print(len([1, 2, 3]))  # ← тепер зламається: len більше не функція

Присвоєння len = "oops" перекриває вбудовану функцію len іменем у Global — після цього len(...) викличе TypeError, бо len тепер рядок, а не функція.

Пастка 2 — UnboundLocalError

x = 10
def test():
    print(x)  # ❌ впаде ще до цього рядка
    x = 5

test()

Результат:

UnboundLocalError: cannot access local variable 'x' where it is not associated with a value

Python аналізує тіло функції заздалегідь і бачить, що всередині є присвоювання x = 5. Тому він вважає x локальною змінною на всю функцію, і рядок print(x) намагається прочитати ще не створену локальну змінну — навіть якщо x існує на глобальному рівні.

Рішення — global:

x = 10
def test():
    global x
    x = 5

test()
print(x)

Результат:

5

Рішення для Enclosing — nonlocal:

def outer():
    x = 10
    def inner():
        nonlocal x
        x = 20
    inner()
    print(x)

outer()

Результат:

20

Ментальна модель

Коли Python бачить ім'я, він перевіряє по черзі:

  1. Чи є воно в Local?
  2. Якщо ні — в Enclosing?
  3. Якщо ні — в Global?
  4. Якщо ні — в Built-in?
  5. Якщо ні — NameError.

LEGB — це не про "де змінна створена". Це про де Python її шукає: ім'я → namespace → об'єкт у пам'яті. LEGB визначає, в якому namespace шукати і яке ім'я пов'язане з яким об'єктом.


👉 Базове (коротше) пояснення scope і LEGB у контексті визначення функцій — на сторінці Функції та функціональне програмування.