❌ Ошибка 1: путаница в порядке родителей
class SaveMixin:
def process(self) -> None:
print("Saving...")
class PrintMixin:
def process(self) -> None:
print("Printing...")
class Document(PrintMixin, SaveMixin): # ожидали SaveMixin?
pass
doc = Document()
doc.process() # Printing...
Почему возникает: Python выбирает метод первого подходящего родителя слева направо. Если порядок не соответствует ожиданиям, вызывается не тот метод.
✅ Исправление
class Document(SaveMixin, PrintMixin):
pass
doc = Document()
doc.process() # Saving...
Проверяйте порядок родителей и используйте Class.__mro__, чтобы увидеть реальный порядок.
❌ Ошибка 2: конфликты имён между родителями
class Logger:
def log(self, message: str) -> None:
print(f"[LOGGER] {message}")
class FileManager:
def log(self, filename: str) -> None:
with open(filename, "w") as file:
file.write("log entry")
class Worker(Logger, FileManager):
pass
worker = Worker()
worker.log("data.txt") # печатает [LOGGER] data.txt, а не пишет в файл
Почему возникает: одинаковые имена методов с разной семантикой. Python не сообщает об ошибке, просто вызывает первый метод в MRO.
✅ Исправление
class Worker(Logger, FileManager):
def write_log(self, filename: str) -> None:
FileManager.log(self, filename)
def print_log(self, message: str) -> None:
Logger.log(self, message)
Переименуйте методы или явно указывайте класс при вызове, если семантика различается.
❌ Ошибка 3: миксин с состоянием
class CounterMixin:
def __init__(self) -> None:
self.count = 0 # плохо: миксин хранит состояние
def increment(self) -> None:
self.count += 1
class User(CounterMixin):
def __init__(self, username: str) -> None:
self.username = username
# super().__init__() легко забыть
Почему возникает: если наследник не вызовет super().__init__(), атрибут count не появится. А если несколько миксинов определяют __init__, их нужно вызывать в правильном порядке.
✅ Исправление
class CounterMixin:
def increment(self) -> None:
self.count += 1
class User(CounterMixin):
def __init__(self, username: str) -> None:
self.username = username
self.count = 0
Миксины должны добавлять поведение, а состояние лучше определять в основном классе.
❌ Ошибка 4: неправильное ожидание от super()
class A:
def action(self) -> None:
print("A")
class B(A):
def action(self) -> None:
print("B")
super().action()
class C(A):
def action(self) -> None:
print("C")
super().action()
class D(C, B): # порядок изменён
def action(self) -> None:
print("D")
super().action()
d = D()
d.action() # D C B A — B вызывается из-за MRO!
Почему возникает: super() идёт по MRO, а не к «ближайшему родителю». В MRO для D(C, B) после C идёт B, поэтому super() внутри C.action() вызывает B.action(), а не сразу A.action().
✅ Исправление
class D(B, C):
def action(self) -> None:
print("D")
super().action()
D().action() # D B C A
Проверяйте D.__mro__ после изменения порядка родителей.
❌ Ошибка 5: явный вызов родителя вместо super()
class Base:
def action(self) -> None:
print("Base")
class Child(Base):
def action(self) -> None:
print("Child")
Base.action(self) # жёсткая привязка
class GrandChild(Child):
pass
GrandChild().action() # Child Base — пока работает
Почему возникает: при добавлении нового родителя между Child и Base вызов всё равно пойдёт в Base, пропустив промежуточный класс.
✅ Исправление
class Child(Base):
def action(self) -> None:
print("Child")
super().action()
super() учитывает актуальный MRO и работает корректно при изменении иерархии.
❌ Ошибка 6: путаница композиции и наследования
class Engine:
def start(self) -> None:
print("Engine started")
class Car(Engine): # машина не является двигателем
pass
car = Car()
car.start()
Почему возникает: «машина» — это не «двигатель», а «содержит двигатель». Наследование здесь семантически неверно.
✅ Исправление
class Engine:
def start(self) -> None:
print("Engine started")
class Car:
def __init__(self) -> None:
self.engine = Engine()
def start(self) -> None:
self.engine.start()
Используйте наследование для «является», а композицию — для «содержит».