В чем разница между локальной переменной и именами?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Локальная переменная vs Имена (Names/Bindings)
Это базовый, но важный концепт о том, как Python управляет переменными и памятью. "Имена" — это ключевое слово для понимания Python.
Основная идея
В Python нет "переменных" в классическом смысле. Есть имена (names), которые привязаны (bound) к объектам в памяти.
# Это НЕ создаёт переменную с значением 10
# Это создаёт имя 'x', которое привязано к объекту int(10)
x = 10
print(id(x)) # Адрес объекта в памяти
print(type(x)) # <class 'int'>
print(id(10)) # Тот же адрес! Потому что это ОДИН объект
Локальные переменные (Local Variables)
Локальные переменные — это имена, определённые в локальной области видимости (функция, класс):
def my_function():
x = 10 # Локальное имя
y = 20 # Локальное имя
def inner():
z = 30 # Локальное имя во вложенной функции
return x + y + z # Может обращаться к x, y (enclosure) и z (local)
return inner()
my_function()
print(x) # NameError: x не определена в глобальной области
Что такое "имена" (Names/Bindings)?
Имя — это ссылка на объект, живущий в памяти. Одно имя может быть переназначено, несколько имён могут указывать на один объект:
# Одно имя, несколько объектов (последовательно)
x = 10
print(id(x)) # Адрес объекта int(10)
x = "hello" # Имя x теперь указывает на другой объект
print(id(x)) # Адрес объекта str("hello")
# Несколько имён, один объект
y = 10
print(id(10)) # Адрес int(10)
print(id(y)) # Тот же адрес!
print(y is x) # False (y указывает на 10, x на "hello")
# Несколько имён на один объект
list1 = [1, 2, 3]
list2 = list1 # Оба имена указывают на ОДИН список
print(list1 is list2) # True
list1.append(4)
print(list2) # [1, 2, 3, 4] — список изменился везде
Область видимости (Scope)
Локальные имена ограничены областью видимости:
x = "global" # Глобальное имя
def function1():
x = "local" # Локальное имя в function1
print(x) # "local"
def function2():
print(x) # "global" — нет локального x, берём из внешней области
function1() # "local"
function2() # "global"
print(x) # "global"
LEGB правило (scope resolution)
Когда Python ищет имя, он проходит через области в таком порядке:
# L — Local (локальная область функции)
# E — Enclosing (внешняя область вложенной функции)
# G — Global (глобальная область модуля)
# B — Built-in (встроенные функции и типы)
global_var = "global"
def outer():
enclosing_var = "enclosing"
def inner():
local_var = "local"
# При обращении к переменной Python ищет в порядке LEGB
print(local_var) # L — найдёт локальную
print(enclosing_var) # E — найдёт в enclosing
print(global_var) # G — найдёт в global
print(len) # B — встроенная функция
return inner
inner_func = outer()
inner_func()
Изменение значения в разных областях
x = 10 # Глобальное
def func1():
x = 20 # Локальное, НЕ изменяет глобальное
print(x) # 20
def func2():
global x # Говорим Python: используем глобальное имя
x = 30 # Изменяем глобальное
print(x) # 30
def func3():
nonlocal x # Используется в nested функциях
x = 40 # Изменяет enclosing область
func1()
print(x) # 10 — глобальное не изменилось
func2()
print(x) # 30 — глобальное изменилось
# nonlocal пример
def outer():
x = 50
def inner():
nonlocal x
x = 60
inner()
print(x) # 60
outer()
Изменяемые vs неизменяемые объекты
Локальные имена могут указывать на изменяемые или неизменяемые объекты:
# Неизменяемые (immutable)
x = 10 # int
x += 5 # Создаёт новый объект int(15), x указывает на него
s = "hello"
s += " world" # Создаёт новый объект str("hello world")
# Изменяемые (mutable)
my_list = [1, 2, 3] # Локальное имя
my_list.append(4) # Изменяет объект, на который указывает мне
print(my_list) # [1, 2, 3, 4]
my_list = [5, 6] # Переназначение имени на новый объект
# Передача в функцию
def modify_list(lst):
lst.append(99) # Изменяет исходный объект
data = [1, 2, 3]
modify_list(data)
print(data) # [1, 2, 3, 99]
Жизненный цикл локального имени
def example():
# 1. Имя x создано и привязано к объекту int(10)
x = 10
print(locals()) # {'x': 10}
# 2. Имя переназначено на другой объект
x = 20
print(locals()) # {'x': 20}
# 3. Имя удалено
del x
print(locals()) # {}
# print(x) # NameError
example()
Практические примеры
Проблема 1: Неожиданное переиспользование имена
# ❌ Плохо
def process_data(data):
result = [] # Локальное имя
for i, item in enumerate(data):
result.append(item * 2)
# Переиспользуем имя для чего-то совсем другого
result = sum(result) # Теперь result — число, не список
return result
# ✅ Хорошо
def process_data(data):
doubled = [item * 2 for item in data] # Ясное имя
total = sum(doubled) # Другое имя
return total
Проблема 2: Closure и локальные имена
# ❌ Классическая ошибка с closure
functions = []
for i in range(3):
def func():
return i # Захватывает имя i, не значение
functions.append(func)
for f in functions:
print(f()) # 2, 2, 2 (i везде 2)
# ✅ Правильно: захвати значение
functions = []
for i in range(3):
def make_func(val):
def func():
return val
return func
functions.append(make_func(i))
for f in functions:
print(f()) # 0, 1, 2
# Или через default argument
functions = []
for i in range(3):
def func(val=i): # val получает ТЕКУЩЕЕ значение i
return val
functions.append(func)
for f in functions:
print(f()) # 0, 1, 2
Проверка областей
def check_scope():
x = 10
y = 20
# locals() — локальные имена
print(locals()) # {'x': 10, 'y': 20, 'check_scope': <function>}
# globals() — глобальные имена
print(globals()) # Все имена на модульном уровне
check_scope()
Итоговое различие
| Аспект | Локальная переменная | Имя (Binding) |
|---|---|---|
| Что это | Область видимости | Ссылка на объект |
| Жизненный цикл | До конца функции | Пока существует ссылка |
| Может менять значение | Да | Да (переназначение) |
| Может быть удалено | Да (del) | Да (del) |
| Видимо в locals() | Да | Да |
Ключ к пониманию Python: переменные — это просто имена, указывающие на объекты. Отпусти идею о "переменной как контейнера» и поймёшь Python.