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

Почему нельзя обратиться к переменной функции вне функции?

1.3 Junior🔥 171 комментариев
#Python Core

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

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

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

Почему нельзя обратиться к переменной функции вне функции?

Переменные, объявленные внутри функции, имеют локальную область видимости (scope) и существуют только в течение выполнения функции. Это фундаментальный принцип структурированного программирования, обеспечивающий изоляцию данных и предотвращающий конфликты имён.

Причины: область видимости (scope)

Память

Переменные функции хранятся в stack frame (кадр стека), который создаётся при вызове функции и удаляется после её завершения:

def my_function():
    x = 10  # Переменная x в стеке
    print(x)  # Доступна здесь

my_function()  # При вызове создаётся stack frame
# После выхода из функции stack frame удаляется

print(x)  # NameError: name 'x' is not defined

Визуально:

Вызов my_function():
Stack Frame (my_function):
  ┌─────────────┐
  │ x = 10      │
  │ y = 20      │
  └─────────────┘

После return:
Stack Frame удалена, переменные x и y больше недоступны

Область видимости (scope)

Python использует LEGB rule для поиска переменной:

  1. Local — локальная область функции
  2. Enclosing — области вложенных функций
  3. Global — глобальная область модуля
  4. Built-in — встроенные функции Python
x = 'global'  # Global scope

def outer():
    x = 'enclosing'  # Enclosing scope
    
    def inner():
        x = 'local'  # Local scope
        print(x)  # Ищет в: Local -> Enclosing -> Global -> Built-in
    
    inner()
    print(x)

outer()
print(x)

# Результат:
# local (нашла в Local scope)
# enclosing (нашла в Enclosing scope)
# global (нашла в Global scope)

Примеры

Локальные переменные

def calculate():
    result = 10 + 20  # Локальная переменная
    return result

print(calculate())  # 30 (OK)
print(result)  # NameError: name 'result' is not defined

Глобальные переменные

x = 'global'  # Глобальная переменная

def access_global():
    print(x)  # Может читать

access_global()  # global
print(x)  # global

Попытка изменить глобальную переменную

counter = 0  # Global

def increment():
    counter += 1  # UnboundLocalError!
    return counter

increment()
# UnboundLocalError: local variable 'counter' referenced before assignment

Почему ошибка? Python видит присваивание counter += 1, поэтому считает counter локальной переменной. Но в начале функции она ещё не определена!

Решение с global:

counter = 0

def increment():
    global counter  # Говорим: используем глобальную переменную
    counter += 1
    return counter

print(increment())  # 1
print(increment())  # 2
print(counter)  # 2

Вложенные функции и nonlocal

def outer():
    x = 10
    
    def inner():
        # x = x + 1  # UnboundLocalError!
        
        # Решение: nonlocal
        nonlocal x
        x = x + 1
        return x
    
    result = inner()
    print(f"x после inner: {x}")  # 11
    return result

outer()  # 11

Замыкания (closures)

Вложенная функция может запомнить переменные из внешней функции:

def make_multiplier(n):
    def multiplier(x):
        return x * n  # x локальная, n из enclosing scope
    return multiplier

times_three = make_multiplier(3)
print(times_three(10))  # 30

times_five = make_multiplier(5)
print(times_five(10))  # 50

# Каждое замыкание помнит свой n!
print(times_three.n)  # AttributeError
print(times_three.__closure__)  # (<cell at ...: int object at ...>,)

Жизненный цикл переменной

def demonstrate_lifetime():
    x = 'created'  # Создание переменной
    print(x)  # Использование
    # x = None  # Можно явно удалить
    # del x  # Или использовать del

demonstrate_lifetime()
# После выхода из функции x удаляется из памяти
# (кроме случаев, когда на неё есть ссылки в замыканиях)

Почему это необходимо

1. Изоляция данных

def function_a():
    data = 'secret_a'
    process(data)

def function_b():
    data = 'secret_b'  # Не конфликтует с function_a
    process(data)

# Переменные data в разных функциях независимы

2. Предотвращение конфликтов имён

i = 'global i'

def loop():
    i = 0  # Локальная переменная
    for i in range(5):  # Не затирает глобальный i
        pass
    print(i)  # 4

loop()
print(i)  # 'global i' (не изменился)

3. Управление памятью

def process_large_file():
    large_data = read_large_file()  # Много памяти
    result = analyze(large_data)
    return result
    # large_data удаляется после return
    # Память освобождается

4. Предотвращение побочных эффектов

# Плохо: глобальное состояние
cache = {}

def function1():
    cache['key'] = 'value'  # Побочный эффект

def function2():
    if 'key' in cache:  # Зависит от function1
        ...

# Хорошо: переменные в функции
def function_good():
    cache = {}  # Локальное состояние
    cache['key'] = 'value'
    return cache

Как сделать переменную доступной снаружи

Вариант 1: Вернуть значение

def get_value():
    x = 10
    return x  # Возвращаем значение

x = get_value()
print(x)  # 10

Вариант 2: Использовать global

x = None

def set_value():
    global x
    x = 10

set_value()
print(x)  # 10

Вариант 3: Использовать объект (класс)

class Config:
    value = None

def set_config():
    Config.value = 10  # Изменяем атрибут класса

set_config()
print(Config.value)  # 10

Вариант 4: Возвращение объекта

def create_object():
    class LocalClass:
        x = 10
    return LocalClass()  # Возвращаем объект

obj = create_object()
print(obj.x)  # 10

Таблица видимости

Место объявленияДоступна внутриДоступна снаружиПример
Внутри функцииДаНетdef f(): x = 1
Вне функции (модуль)Нет (нужен global)Даx = 1
КлассЧерез selfНет (нужен объект)class C: x = 1
Вложенная функцияДа (nonlocal)Нетdef f(): def g(): x = 1

Заключение

  1. Локальная область видимости создаётся при вызове функции
  2. Стек удаляется после выхода из функции
  3. LEGB rule определяет порядок поиска переменной
  4. global и nonlocal позволяют доступ к переменным других областей
  5. Это безопасность — предотвращает конфликты и случайные изменения
  6. Это производительность — память освобождается автоматически

Это не ошибка Python, а правильное поведение, обеспечивающее безопасность и чистоту кода.