← Назад к вопросам
Что произойдет, если написать одну и ту же функцию, что и у родительского класса в Python?
2.0 Middle🔥 81 комментариев
#Python Core#Soft Skills#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Method Override (переопределение методов в Python)
Это механизм полиморфизма. Когда дочерний класс определяет метод с тем же именем, он переопределяет (override) родительский метод.
Базовый пример: Override
class Animal:
def speak(self):
return "Some generic sound"
class Dog(Animal):
def speak(self): # Переопределяем метод родителя
return "Woof!"
class Cat(Animal):
def speak(self): # Переопределяем метод родителя
return "Meow!"
# Использование
dog = Dog()
cat = Cat()
generic = Animal()
print(dog.speak()) # "Woof!" (метод Dog)
print(cat.speak()) # "Meow!" (метод Cat)
print(generic.speak()) # "Some generic sound" (метод Animal)
Что происходит:
- Python ищет метод в самом классе первым
- Если найден → использует этот метод
- Если не найден → ищет в родительском классе
- Если не найден там → ищет выше по MRO (Method Resolution Order)
Method Resolution Order (MRO)
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass # Не переопределяет
# MRO: как Python ищет методы
print(D.mro()) # [D, B, C, A, object]
d = D()
print(d.method()) # "B" (первый в MRO после D)
# Проверить MRO
print(D.__mro__) # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Override с super() — правильный способ
class Parent:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, I'm {self.name}"
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # Вызываем инициализацию родителя
self.age = age
def greet(self): # Override
# Вызываем родительский метод И добавляем свое
parent_greeting = super().greet()
return f"{parent_greeting} and I'm {self.age} years old"
# Использование
child = Child("Alice", 10)
print(child.greet())
# "Hello, I'm Alice and I'm 10 years old"
Почему super() важна
# ❌ НЕПРАВИЛЬНО: не используем super()
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
# Проблема: если Parent меняется, мы забудем обновить
Parent.__init__(self, name) # Хардкодим имя класса
self.age = age
# ✅ ПРАВИЛЬНО: используем super()
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # Автоматически найдет Parent
self.age = age
Полиморфизм в действии
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""Все фигуры должны реализовать эту функцию"""
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # Override абстрактного метода
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self): # Override абстрактного метода
return self.width * self.height
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self): # Override абстрактного метода
return 0.5 * self.base * self.height
# Полиморфизм: один код работает с разными типами
def calculate_total_area(shapes: list[Shape]) -> float:
"""Работает с любыми Shape, используя их override методы"""
total = 0
for shape in shapes:
total += shape.area() # Вызывает правильный area() для каждого класса
return total
# Использование
shapes = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 4)
]
print(calculate_total_area(shapes))
# Каждый shape.area() вызовет свою реализацию!
Override с разными сигнатурами
class Parent:
def method(self, x):
return x * 2
# Можно переопределить с разными параметрами
class Child(Parent):
def method(self, x, y=10): # Добавили параметр
return x * y
child = Child()
print(child.method(5)) # 50 (использует y=10)
print(child.method(5, 20)) # 100 (использует y=20)
# Но это не очень хорошая практика!
# Лучше использовать *args и **kwargs
class Child2(Parent):
def method(self, x, *args, **kwargs):
result = super().method(x) # Вызываем родителя
# Дополнительная обработка
return result
Method Override vs Monkey Patching
# Override (правильно)
class Logger:
def log(self, message):
print(f"[LOG] {message}")
class CustomLogger(Logger):
def log(self, message): # Override
print(f"[CUSTOM LOG] {message}")
# Monkey patching (плохо, не делать!)
class Logger:
def log(self, message):
print(f"[LOG] {message}")
# ❌ Меняем метод во время выполнения (очень плохо!)
Logger.log = lambda self, message: print(f"[PATCHED] {message}")
logger = Logger()
logger.log("test") # [PATCHED] test
# Проблемы с monkey patching:
# - Непредсказуемое поведение
# - Сложнее дебагить
# - Нарушает контракт класса
# - Может сломать код, который полагается на оригинальный метод
Проверка, переопределена ли функция
class Parent:
def method(self):
return "parent"
class Child1(Parent):
pass # Не переопределяет
class Child2(Parent):
def method(self): # Переопределяет
return "child2"
# Способ 1: простой check
print(Child1.method == Parent.method) # True (наследует)
print(Child2.method == Parent.method) # False (переопределяет)
# Способ 2: проверить в __dict__
print('method' in Child1.__dict__) # False
print('method' in Child2.__dict__) # True
# Способ 3: использовать inspect
import inspect
for name, method in inspect.getmembers(Child2, predicate=inspect.ismethod):
if name == 'method':
# Узнаем где определен метод
owner = next(cls for cls in Child2.__mro__ if name in cls.__dict__)
print(f"Method defined in: {owner}")
Override в классах с множественным наследованием
class Mixin1:
def process(self):
return "mixin1"
class Mixin2:
def process(self):
return "mixin2"
class Base:
def process(self):
return "base"
# MRO определяет какой process() будет вызван
class Combined(Mixin1, Mixin2, Base):
pass
print(Combined().process()) # "mixin1" (первый в MRO)
print(Combined.__mro__)
# (<class 'Combined'>, <class 'Mixin1'>, <class 'Mixin2'>, <class 'Base'>, <class 'object'>)
Практический пример: обработка платежей
class PaymentProcessor:
"""Базовый процессор платежей"""
def validate_payment(self, payment):
return len(payment.get('card_number', '')) == 16
def process(self, payment, amount):
if not self.validate_payment(payment):
raise ValueError("Invalid payment")
return self._charge(amount)
def _charge(self, amount):
# Базовая реализация
print(f"Charging {amount}")
return True
class StripeProcessor(PaymentProcessor):
"""Override для Stripe"""
def validate_payment(self, payment):
# Stripe требует token вместо card number
return 'token' in payment and len(payment['token']) > 0
def _charge(self, amount):
# Stripe специфичная логика
print(f"Stripe charging {amount} using API")
return True
class PayPalProcessor(PaymentProcessor):
"""Override для PayPal"""
def validate_payment(self, payment):
# PayPal требует email
return '@' in payment.get('email', '')
def _charge(self, amount):
# PayPal специфичная логика
print(f"PayPal charging {amount} using email")
return True
# Использование
stripe = StripeProcessor()
paypal = PayPalProcessor()
# Каждый процессор использует свою validate_payment!
print(stripe.process({'token': 'tok123'}, 100)) # True
print(paypal.process({'email': 'user@example.com'}, 50)) # True
Что НЕ переопределяется
class Parent:
def method(self):
return "parent"
class Child(Parent):
method = "this is not a method" # ❌ НЕПРАВИЛЬНО!
child = Child()
try:
child.method() # TypeError: 'str' object is not callable
except TypeError as e:
print(f"Error: {e}")
# Правильно:
class Child(Parent):
def method(self): # Это переопределяет
return "child"
Best Practices для Override
-
Всегда используй super() для вызова родительского метода
class Child(Parent): def method(self): result = super().method() # ✓ return result + " extended" -
Сохраняй одинаковую сигнатуру метода
# ✓ Хорошо: параметры совпадают class Parent: def method(self, x): pass class Child(Parent): def method(self, x): super().method(x) # ❌ Плохо: разные параметры class Child(Parent): def method(self, x, y): pass -
Используй abstractmethod для обязательного override
from abc import ABC, abstractmethod class Base(ABC): @abstractmethod def required_method(self): pass -
Документируй что переопределяешь
class Child(Parent): def process(self): """Override: добавляем логирование к процессу""" logger.info("Starting process") result = super().process() logger.info(f"Process completed: {result}") return result
Вывод: Override метода в дочернем классе — это стандартный полиморфизм в Python. Используй super() для корректной работы с наследованием.