← Назад к вопросам
Как работает ссылочная модель памяти в Python?
1.8 Middle🔥 131 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ссылочная модель памяти в Python
В Python всё, что угодно — целые числа, строки, функции, классы — это объекты в памяти. Переменные не хранят сами значения, а содержат ссылки (references) на объекты. Это фундаментальное различие от языков типа C.
Объекты и ссылки
Базовая концепция
# Это не присваивание значения, а создание ссылки на объект
x = [1, 2, 3] # x указывает на список
y = x # y указывает на ТОТ ЖЕ список
print(x is y) # True — одинаковые объекты в памяти
print(id(x) == id(y)) # True — одинаковые адреса
# Изменение через одну ссылку видно в другой
y.append(4)
print(x) # [1, 2, 3, 4] — изменилось!
Важное различие: изменяемые vs неизменяемые объекты
# Неизменяемые (immutable): int, str, tuple, frozenset
a = 10
b = a
b = 20 # Это создаёт НОВЫЙ объект, не меняет существующий
print(a) # 10 — a не изменилась
print(id(a) != id(b)) # True — разные объекты
# Изменяемые (mutable): list, dict, set
x = [1, 2]
y = x
y[0] = 99 # Меняет существующий объект
print(x) # [99, 2] — изменилось через x!
print(id(x) == id(y)) # True — всё ещё одинаковые объекты
Глубокое копирование vs поверхностное
import copy
# Поверхностное копирование (shallow copy)
original = [1, [2, 3], 4]
shallow = original.copy() # или list(original)
shallow[0] = 99
print(original) # [1, [2, 3], 4] — первый уровень не затронут
shallow[1][0] = 99
print(original) # [1, [99, 3], 4] — вложенные объекты МЕНЯЮТСЯ!
# Глубокое копирование (deep copy)
original = [1, [2, 3], 4]
deep = copy.deepcopy(original)
deep[1][0] = 99
print(original) # [1, [2, 3], 4] — НИЧЕГО не изменилось
print(deep) # [1, [99, 3], 4]
Счётчик ссылок (Reference Counting)
import sys
# sys.getrefcount() показывает количество ссылок на объект
x = [1, 2, 3]
print(sys.getrefcount(x)) # Обычно 2 (сама переменная + аргумент функции)
y = x
print(sys.getrefcount(x)) # Теперь 3 (x, y, и аргумент функции)
del y
print(sys.getrefcount(x)) # Вернулось к 2
Garbage Collection
Когда счётчик ссылок объекта становится нулём, Python автоматически удаляет объект:
class MyClass:
def __del__(self):
print("Объект удалён из памяти")
x = MyClass()
print("x создана") # Объект существует
del x # Удаляем ссылку
# Вывод: "Объект удалён из памяти"
# Циклические ссылки требуют помощи
class Node:
def __init__(self, value):
self.value = value
self.next = None
a = Node(1)
b = Node(2)
a.next = b
b.next = a # Циклическая ссылка!
# Без явного удаления, garbage collector обнаружит и удалит
del a, b
Параметры функций (pass by object reference)
def modify_list(lst):
lst.append(999) # Меняет исходный список
def reassign_list(lst):
lst = [7, 8, 9] # Создаёт новый локальный объект
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # [1, 2, 3, 999] — изменилось
my_list = [1, 2, 3]
reassign_list(my_list)
print(my_list) # [1, 2, 3] — НЕ изменилось
Область видимости (Scope)
x = 10 # Глобальная переменная
def func():
x = 20 # Локальная переменная (новый объект)
print(x) # 20
func()
print(x) # 10 — глобальная не изменилась
# Изменение глобальной переменной
def modify_global():
global x
x = 30
modify_global()
print(x) # 30
# Нелокальные переменные (nonlocal) в замыканиях
def outer():
x = 10
def inner():
nonlocal x # Ссылка на x из outer()
x = 20
inner()
return x
print(outer()) # 20
Ссылки и контейнеры
# Список содержит ссылки на объекты, не сами объекты
obj = {"key": "value"}
my_list = [obj, obj, obj] # Три ссылки на ОДИН словарь
obj["key"] = "changed"
print(my_list[0]["key"]) # "changed" — все три элемента изменились
# Словарь может содержать себя
my_dict = {"a": 1}
my_dict["self"] = my_dict # Циклическая ссылка
Сравнение == и is
# == сравнивает ЗНАЧЕНИЯ
# is сравнивает ИДЕНТИЧНОСТЬ (тот же объект в памяти)
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — одинаковые значения
print(a is b) # False — разные объекты
print(id(a) != id(b)) # True
# Исключение: малые целые числа и строки кешируются
c = 256
d = 256
print(c is d) # True (Python кеширует числа от -5 до 256)
e = 257
f = 257
print(e is f) # False (обычно, в разных условиях)
Практический пример: неправильное использование значений по умолчанию
# ОШИБКА: изменяемый объект как значение по умолчанию
def add_item(item, container=[]):
container.append(item) # Один список на всех!
return container
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — ошибка!
print(add_item(3)) # [1, 2, 3] — ошибка!
# ПРАВИЛЬНО: None как значение по умолчанию
def add_item_correct(item, container=None):
if container is None:
container = []
container.append(item)
return container
print(add_item_correct(1)) # [1]
print(add_item_correct(2)) # [2]
Память и производительность
import sys
# Размер объекта в памяти
print(sys.getsizeof(42)) # 28 bytes (для int)
print(sys.getsizeof("hello")) # 54 bytes (для str)
print(sys.getsizeof([1, 2, 3])) # 56 bytes (для list)
# Ссылка занимает 8 байт на 64-битной системе
# Но внутренние структуры требуют памяти для счётчика ссылок, типа и т.д.
Лучшие практики
- Помни про изменяемые параметры — передавай копии если нужно
- Используй copy.deepcopy() для глубокого копирования
- Проверяй с is только None, True, False — для остального используй ==
- Будь осторожен с изменяемыми значениями по умолчанию
- Понимай разницу между modify и reassign в функциях
- Разбирайся в циклических ссылках если работаешь со сложными структурами
Понимание ссылочной модели памяти критически важно для написания корректного Python кода и отладки ошибок, связанных с неожиданным изменением данных.