Простори імен: locals(), globals() і правило LEGB¶
locals() та globals() — вбудовані інструменти інтроспекції, які дозволяють заглянути "під капот" вашої програми. Вони показують:
- які змінні зараз існують;
- де саме вони існують;
- у якому просторі імен вони зберігаються.
Обидві функції повертають словник (dict), де ключі — це імена змінних (рядки), а значення — об'єкти, на які ці імена посилаються.
Передумова
Якщо ще не знайомі з базовим поняттям області видимості (scope) і скороченою версією LEGB — прочитайте спершу Функції → 3. Область видимості. Тут — повний розбір із прикладами на кожен рівень.
Що таке простір імен (namespace)?¶
Namespace — це словник, який зв'язує ім'я → об'єкт у пам'яті. У Python є кілька рівнів:
- Built-in namespace
- Global namespace
- Local namespace
globals() — глобальний простір імен¶
Повертає словник змінних, функцій і класів, визначених на рівні модуля (файлу). Глобальні змінні існують весь час роботи програми та доступні з будь-якого місця модуля.
На верхньому рівні скрипта locals() і globals() — це буквально той самий словник:
Результат:
Це так, бо ми ще не всередині функції — на рівні модуля локальний і глобальний простір імен збігаються.
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() повертає той самий словник, яким Python користується для зберігання глобальних імен, тож запис у нього одразу видно решті програми.
locals() працює лише для читання¶
def test():
locals()['x'] = 5
print("locals() dict:", locals())
try:
print("реальне ім'я x:", x)
except NameError as e:
print("NameError:", e)
test()
Результат:
Тут легко зробити неправильний висновок із того, що print(locals()) показав {'x': 5} — ніби запис спрацював. Насправді ні: усередині функції locals() повертає копію локального простору імен, і саме в цю копію запис і потрапив. Ключове підтвердження — спроба звернутися до x напряму як до імені одразу падає з NameError: реальної локальної змінної x не існує, є лише запис у тимчасовому словнику. Щоб змінити справжню локальну змінну, потрібне звичайне присвоювання: x = 5.
Практичне використання¶
1. Швидкий дебаг¶
Якщо функція працює неправильно, locals() дозволяє побачити весь стан функції в один момент:
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)
Результат:
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"))
Результат:
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)
Результат:
Python спочатку шукає в Local → знаходить "local".
2. Enclosing¶
Результат:
inner() не має власного x, тому Python піднімається до Enclosing.
3. Global¶
Результат:
x не знайдено в Local → немає Enclosing → береться з Global.
4. Built-in¶
Результат:
len не визначений у Local, Enclosing чи Global, тому Python бере його з Built-in.
Дві типові пастки¶
Пастка 1 — перекриття built-in¶
Присвоєння len = "oops" перекриває вбудовану функцію len іменем у Global — після цього len(...) викличе TypeError, бо len тепер рядок, а не функція.
Пастка 2 — UnboundLocalError¶
Результат:
Python аналізує тіло функції заздалегідь і бачить, що всередині є присвоювання x = 5. Тому він вважає x локальною змінною на всю функцію, і рядок print(x) намагається прочитати ще не створену локальну змінну — навіть якщо x існує на глобальному рівні.
Рішення — global:
Результат:
Рішення для Enclosing — nonlocal:
Результат:
Ментальна модель¶
Коли Python бачить ім'я, він перевіряє по черзі:
- Чи є воно в Local?
- Якщо ні — в Enclosing?
- Якщо ні — в Global?
- Якщо ні — в Built-in?
- Якщо ні —
NameError.
LEGB — це не про "де змінна створена". Це про де Python її шукає: ім'я → namespace → об'єкт у пам'яті. LEGB визначає, в якому namespace шукати і яке ім'я пов'язане з яким об'єктом.
👉 Базове (коротше) пояснення scope і LEGB у контексті визначення функцій — на сторінці Функції та функціональне програмування.