← Назад к вопросам

Что такое область видимости?

1.0 Junior🔥 231 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое область видимости (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 делает код более понятным, безопасным и проще поддерживаемым.

Что такое область видимости? | PrepBro