Глава 14 · Создаём объекты реального мира
Ловушка общего изменяемого атрибута
Один список «на двоих» — классический баг ООП-новичка. Разбираем, откуда он берётся и как его избежать.
Когда «общий атрибут» превращается в баг
Атрибут класса с НЕИЗМЕНЯЕМЫМ значением (число, строка) безопасен — присваивание через
объект просто создаёт новый атрибут экземпляра (раздел 14.7). Но атрибут класса со
ИЗМЕНЯЕМЫМ значением — список или словарь — устроен иначе: если его не
ПЕРЕПРИСВОИТЬ, а ИЗМЕНИТЬ на месте (.append(), [key] = ...), это затронет объект класса, который используют ВСЕ
экземпляры сразу.
Debug Lab 3: общий изменяемый список как атрибут класса
obshchaya_korzina.py
class Korzina:
tovary = [] # атрибут КЛАССА — один список на все корзины!
def dobavit(self, tovar):
self.tovary.append(tovar) # .append() меняет список НА МЕСТЕ
k1 = Korzina()
k2 = Korzina()
k1.dobavit("Хлеб")
print(k2.tovary)
>>> print(k2.tovary)
['Хлеб']
k2 ни разу не вызывал dobavit(), но «Хлеб» уже в его корзине! Причина: self.tovary.append(...) не создаёт новый список — он ИЗМЕНЯЕТ существующий, а этот список один-единственный, унаследован обоими объектами от класса Korzina.
Исправленный код
svoya_korzina.py
class Korzina:
def __init__(self):
self.tovary = [] # атрибут ЭКЗЕМПЛЯРА — свой список у каждой корзины
def dobavit(self, tovar):
self.tovary.append(tovar)
k1 = Korzina()
k2 = Korzina()
k1.dobavit("Хлеб")
print(k2.tovary) # [] — пусто, как и должно быть
Правило: изменяемые значения — только в __init__
Если атрибуту нужно значение
list, dict или set — создавайте его внутри __init__ через self.имя = [], а не в теле класса. Это тот же самый принцип, что и «ловушка изменяемого значения по умолчанию» у параметров функций (глава 13) — и грабли те же самые.Практика: находим общий изменяемый атрибут
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки