У Python усе є об’єктами. Коли ви створюєте змінну, функцію чи клас, ви фактично надаєте їм імена. Але як інтерпретатор Python визначає, що саме стоїть за тим чи іншим ім’ям? Відповідь криється у просторах імен (namespaces).
Зміст допису:
- Що таке простір імен у Python
- Простір імен (namespace) vs область видимості (scope)
- Реалізація просторів імен у Python
- Типи просторів імен
- Тривалість життя простору імен
- Користувацькі простори імен
Що таке простір імен у Python
У Python простір імен – це структура, яка визначає відповідність між іменами та об’єктами. Його можна уявити як словник, де ключами є імена, а значеннями – об’єкти: змінні, функції, класи, модулі тощо.
Розглянемо приклад коду:
x = 10Коли ви виконуєте цей код, Python додає ім’я x до простору імен і зв’язує його з об’єктом – цілим числом 10. Саме цей процес зіставлення імені з об’єктом відбувається у межах простору імен.
У програмі на Python одночасно може існувати кілька просторів імен. Вони зазвичай організовані відповідно до області видимості (scope) та контексту виконання (execution context).
Простори імен допомагають уникати конфліктів імен у великих програмах. Вони забезпечують ізоляцію: модулі, функції та класи створюють власні простори імен, що дозволяє використовувати однакові імена в різних контекстах без перешкод.
Простір імен (namespace) vs область видимості (scope)
Простір імен та область видимості пов’язані поняття, але не тотожні:
- Простір імен – це відображення імен на об’єкти. Він відповідає на запитання: де зберігається це ім’я?
- Область видимості визначає, з яких частин програми можна отримати доступ до певного імені. Вона відповідає на запитання: чи доступне це ім’я в поточному контексті?
Розглянемо простий приклад:
x = 5
def function():
y = 10
print(x)
print(y)
function()
# 5
# 10- x – знаходиться в глобальному просторі імен
- y – знаходиться в локальному просторі імен function
Для визначення послідовності, в якій Python шукає значення змінної, використовується правило LEGB. Воно описує порядок пошуку імен у таких областях видимості:
- Local – локальна область у межах функції
- Enclosing – області зовнішніх функцій у випадку вкладених функцій
- Global – глобальна область модуля
- Built-in – вбудовані імена Python
Приклад:
x = 'global' # Global
def outer():
x = 'enclosing' # Enclosing
def inner():
x = 'local' # Local
print(x) # Шукає змінну за правилом LEGB
inner()
outer() # Output: localРеалізація просторів імен у Python
У Python простори імен реалізуються за допомогою словників. Це логічно, адже словник зберігає пари ключ–значення, а простір імен – це відповідність між іменами (ключами) та об’єктами (значеннями).
Для доступу до локального та глобального простору імен, можна скористатись функціями globals() та locals():
x = 42
def show_namespaces():
y = 'hello'
print('Local:', locals())
print('Global:', globals())
show_namespaces()
# Local: {'y': 'hello'}
# Global: {'__name__': '__main__', 'x': 42, 'show_namespaces': <function show_namespaces...Завдяки словниковій структурі простору імен Python може динамічно змінювати вміст: додавати нові імена, оновлювати наявні або видаляти їх під час виконання коду.
Функції locals() та globals() можуть бути корисними під час налагодження, вони дозволяють переглянути, які імена доступні у поточному просторі імен.
Типи просторів імен
Python підтримує кілька типів просторів імен, кожен із яких створюється в певному контексті (наприклад, при виклику функції або імпорті модуля) та має свій власний час існування.
Вбудований (Built-in)
Цей простір імен створюється під час запуску інтерпретатора Python. Він містить усі вбудовані функції та винятки. Наприклад функції print() та len() доступні з будь-якого місця програми.
Доступ до вбудованого простору імен можна отримати через модуль __builtins__:
print(dir(__builtins__))
# ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', ...Глобальний (Global)
Кожен модуль у Python має свій власний глобальний простір імен. Він створюється під час завантаження модуля та існує до завершення програми або вивантаження модуля з пам’яті. Змінні та функції, визначені на верхньому рівні модуля, автоматично потрапляють у цей глобальний простір:
x = 5 # глобальна змінна
def foo():
print(x) # Python шукає x спочатку в локальному, потім у глобальному просторі
# 5Локальний (Local)
Під час кожного виклику функції Python створює новий локальний простір імен. Усі імена, визначені в межах тіла функції, зберігаються лише в цьому просторі й недоступні за його межами:
def greet():
name = 'Alice' # локальна змінна
print(f'Hello, {name}')
greet() # Hello, Alice
print(name) # NameError: name 'name' is not definedEnclosing Namespace
Enclosing namespace у Python – це простір імен зовнішньої функції, якщо в її тілі оголошено вкладену функцію. Іншими словами, коли одна функція визначена всередині іншої, вкладена функція має доступ до змінних зовнішньої функції – вони зберігаються саме в enclosing namespace:
def outer():
message = 'Hello from outer'
def inner():
print(message) # шукає 'message' у enclosing namespace
inner()
outer() # Hello from outerТривалість життя простору імен
Простір імен зберігається доти, доки активний контекст, у якому він був створений – наприклад, функція, модуль або інтерпретатор загалом:
- Вбудований простір імен створюється під час запуску інтерпретатора Python та існує протягом усього часу виконання програми. Він містить стандартні функції, винятки та інші вбудовані об’єкти.
- Глобальний простір імен створюється при завантаженні модуля або на початку виконання скрипту. Він зберігається до завершення роботи програми або до вивантаження модуля, якщо його було імпортовано.
- Локальний простір імен виникає під час виклику функції й існує лише протягом часу її виконання. Після завершення функції цей простір зникає.
- Enclosing Namespace – це простір імен зовнішньої функції у випадку вкладених функцій. Він зберігається доки вкладена функція “жива” – наприклад, якщо вона повертається як об’єкт:
def outer():
msg = 'Hi'
def inner():
print(msg)
return inner
# enclosing простір зі змінною msg живе далі, бо inner його використовуєКористувацькі простори імен
У Python можна створювати власні (custom) простори імен за допомогою класів або словників. Це корисно для ізоляції змінних, структурованого зберігання даних або контролю доступу до імен.
1. Custom namespace через клас
Коли ви створюєте клас, Python формує окремий простір імен для його атрибутів. Це є типовим прикладом користувацького простору імен:
class Config:
debug = True
version = '1.0'
host = 'localhost'
print(Config.debug) # True
print(Config.__dict__) # Словник простору імен класуПояснення:
- імена debug, version та host належать простору імен класу Config
- простір імен класу доступний через його атрибут __dict__
2. Custom namespace через словник
У деяких випадках роль простору імен може виконувати звичайний словник. Це зручно, коли потрібна проста структура для зберігання імен, без зайвої функціональності класів:
settings = {
'debug': True,
'version': '1.0',
'host': 'localhost'
}
print(settings['debug']) # True3. Використання модуля types.SimpleNamespace
Клас SimpleNamespace з модуля types дозволяє створити простий об’єкт контейнер, схожий на клас, у якому атрибути можна додавати та змінювати динамічно:
from types import SimpleNamespace
config = SimpleNamespace()
config.debug = True
config.version = '1.0'
print(config.debug) # True
print(vars(config)) # {'debug': True, 'version': '1.0'}Коли використовувати Custom Namespace:
- для зручного групування параметрів (наприклад налаштувань)
- щоб створити ізольоване середовище імен та уникнути конфліктів
- як просту заміну складних класів у випадках де достатньо функціональності класу SimpleNamespace