Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое область видимости (Scope)?
Область видимости (scope) — это часть программы, в которой переменная доступна для использования. Это один из фундаментальных концептов программирования, определяющий, где и как можно обращаться к переменным.
Типы областей видимости в Python
Python использует правило LEGB (Local, Enclosing, Global, Built-in) для поиска переменных.
1. Local Scope (локальная область)
Переменные, определенные внутри функции.
def my_function():
x = 10 # локальная переменная
print(x) # 10
my_function()
print(x) # Error: NameError - переменная не существует в global scope
# ===
def outer():
y = 20
def inner():
z = 30 # локальная переменная для inner()
print(z) # 30 - доступна
print(y) # 20 - доступна из enclosing scope
inner()
print(z) # Error: NameError - z существует только в inner()
outer()
2. Enclosing Scope (вложенная область)
Область видимости функции, которая содержит вложенную функцию (closure).
def outer():
x = 10 # enclosing scope для inner()
def inner():
print(x) # может читать из enclosing scope
inner() # 10
outer()
# === Closure пример ===
def create_multiplier(n):
def multiplier(x):
return x * n # n из enclosing scope
return multiplier
times_3 = create_multiplier(3)
print(times_3(10)) # 30
times_5 = create_multiplier(5)
print(times_5(10)) # 50
3. Global Scope (глобальная область)
Переменные, определенные на уровне модуля (вне функций).
x = 100 # глобальная переменная
def my_function():
print(x) # 100 - может читать глобальную переменную
my_function()
print(x) # 100
# === Изменение глобальной переменной ===
counter = 0
def increment():
global counter # явно говорим, что используем глобальную переменную
counter += 1 # теперь это работает
return counter
print(increment()) # 1
print(increment()) # 2
print(counter) # 2
4. Built-in Scope (встроенная область)
Встроенные функции и типы Python (print, len, list, dict и т.д.).
# Это работает в любом месте программы
print(len([1, 2, 3])) # len из built-in scope
print(type(42)) # type из built-in scope
# Но можно переопределить (не рекомендуется!)
len = lambda x: "custom len" # плохая идея!
print(len([1, 2, 3])) # custom len
# Восстановить встроенную
import builtins
print(builtins.len([1, 2, 3])) # 3
Правило LEGB (поиск переменной)
Когда Python ищет переменную, он смотрит в таком порядке:
# L — Local
# E — Enclosing
# G — Global
# B — Built-in
x = "global" # Global
def outer():
x = "enclosing" # Enclosing для inner()
def inner():
x = "local" # Local
print(x) # Находит local x
inner()
print(x) # Находит enclosing x
outer()
print(x) # Находит global x
print(len) # Built-in функция
Keyword: nonlocal
Для изменения переменной из enclosing scope (не global):
def outer():
x = 10
def inner():
nonlocal x # используем переменную из enclosing scope
x = 20 # изменяем её
inner()
print(x) # 20
outer()
# === Без nonlocal — ошибка ===
def outer2():
y = 10
def inner2():
y = 20 # это создает НОВУЮ локальную переменную
# y в outer2 остается 10
inner2()
print(y) # 10
outer2()
Область видимости в классах
class MyClass:
class_var = "класс" # переменная класса
def __init__(self):
self.instance_var = "экземпляр" # переменная экземпляра
def method(self):
local_var = "метод" # локальная переменная метода
print(local_var) # local_var из method
print(self.instance_var) # через self
print(self.class_var) # через self
print(MyClass.class_var) # через класс
obj = MyClass()
obj.method()
# local_var не доступна снаружи
# instance_var доступна как obj.instance_var
# class_var доступна как MyClass.class_var
Практические примеры
# === Пример 1: Счетчик с closure ===
def make_counter():
count = 0 # enclosing scope
def increment():
nonlocal count # изменяем enclosing переменную
count += 1
return count
def get_count():
return count # просто читаем
return increment, get_count
inc, get = make_counter()
print(inc()) # 1
print(inc()) # 2
print(get()) # 2
# === Пример 2: Декоратор (использует closure) ===
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Вызов {func.__name__}") # func из enclosing scope
result = func(*args, **kwargs)
print(f"Результат: {result}")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
add(2, 3) # prints info about call
# === Пример 3: Избежать проблемы в цикле ===
# Проблема
functions = []
for i in range(3):
def func():
return i # все функции видят одно i
functions.append(func)
print([f() for f in functions]) # [2, 2, 2] - ошибка!
# Решение 1: default argument
functions = []
for i in range(3):
def func(x=i): # x захватывает текущее значение i
return x
functions.append(func)
print([f() for f in functions]) # [0, 1, 2] - правильно!
# Решение 2: closure factory
functions = []
def make_func(x):
def func():
return x
return func
for i in range(3):
functions.append(make_func(i))
print([f() for f in functions]) # [0, 1, 2] - правильно!
Важные моменты
# === Переменная должна быть определена перед использованием ===
print(x) # Error: NameError - x еще не определена
x = 10
# === Глобальная переменная видна в функции ===
x = 10
def func1():
print(x) # 10 - может читать
func1()
# === Но при присваивании создается локальная переменная ===
x = 10
def func2():
x = 20 # создает локальную переменную, не меняет глобальную
print(x) # 20
func2()
print(x) # 10 - глобальная не изменилась!
# === Для изменения глобальной используй global ===
x = 10
def func3():
global x
x = 20 # теперь меняет глобальную
func3()
print(x) # 20
# === Список и словарь меняются без global (объекты mutable) ===
data = [1, 2, 3]
def func4():
data.append(4) # меняет объект, но не саму переменную
func4()
print(data) # [1, 2, 3, 4] - изменился!
# Но переприсвоение требует global
data = [1, 2, 3]
def func5():
global data
data = [5, 6, 7] # требует global для переприсвоения
func5()
print(data) # [5, 6, 7]
Проверка области видимости
# globals() — словарь глобальных переменных
print(globals())
# locals() — словарь локальных переменных
def func():
x = 10
y = 20
print(locals()) # {'x': 10, 'y': 20}
func()
# vars() — для объектов
class MyClass:
x = 10
obj = MyClass()
print(vars(obj)) # {} - нет переменных экземпляра
Лучшие практики
✅ Используй локальные переменные — более предсказуемо ✅ Избегай global — сложнее для отладки ✅ Используй nonlocal для closure — когда это необходимо ✅ Передавай параметры функциям — вместо глобальных переменных ✅ Используй классы для состояния — вместо глобалов
❌ Избегай слишком глубокой вложенности — сложнее понять ❌ Не переопределяй встроенные функции — 'len', 'list' и т.д. ❌ Не полагайся на порядок импорта для инициализации — непредсказуемо
Резюме
Область видимости — это фундаментальный концепт, определяющий, где переменная доступна. Понимание LEGB, global, nonlocal и closure критично для написания хорошего Python кода. Правильное использование scope делает код более понятным, безопасным и проще поддерживаемым.