Что такое переопределение методов (method overriding) в Python?

Ответ

Переопределение метода (method overriding) — это механизм ООП, позволяющий дочернему классу предоставить собственную реализацию метода, который уже определён в родительском классе.

Когда метод вызывается у объекта дочернего класса, исполняется именно его версия, а не версия родительского класса. В Python для этого не нужны специальные ключевые слова — достаточно объявить в дочернем классе метод с тем же именем.

Пример:

class Animal:
    def make_sound(self):
        print("Издает какой-то звук")

class Dog(Animal):
    # Переопределяем метод родительского класса
    def make_sound(self):
        print("Гав! Гав!")

animal = Animal()
dog = Dog()

animal.make_sound() # Вывод: Издает какой-то звук
dog.make_sound()    # Вывод: Гав! Гав!

Расширение, а не замена, с помощью super()

Часто требуется не полностью заменить, а расширить поведение родительского метода. Для вызова реализации из родительского класса используется встроенная функция super().

class Car:
    def __init__(self, brand):
        self.brand = brand
        print(f"Создан автомобиль марки {self.brand}.")

class ElectricCar(Car):
    def __init__(self, brand, battery_size):
        # Сначала вызываем __init__ родительского класса
        super().__init__(brand)
        # Затем добавляем свою логику
        self.battery_size = battery_size
        print(f"Это электромобиль с батареей {self.battery_size} кВт⋅ч.")

my_tesla = ElectricCar("Tesla", 100)

Начиная с Python 3.12, можно использовать декоратор @typing.override для явного указания намерения переопределить метод, что помогает статическим анализаторам находить ошибки.

Ответ 18+ 🔞

Ну вот, смотри, опять эти ваши объектно-ориентированные штуки. Переопределение метода, блядь. Звучит как какая-то бюрократия, а на деле всё просто, как три копейки.

Представь, есть у тебя класс-родитель, типа общего предка. У него есть метод, ну, допустим, make_sound(). Он говорит: "Издает какой-то звук". Ну, типа, неопределёнка, хуйня какая-то.

А теперь появляется его сынок, класс Dog. И этот щенок такой: "Да похуй на твою неопределёнку, отец! Я буду говорить ГАВ!" Вот он и объявляет внутри себя метод с ТАКИМ ЖЕ ИМЕНЕМ — make_sound(), но уже со своей, собачьей реализацией. Это и есть переопределение, ёпта. Когда вызываешь метод у объекта-пёсика, работает уже его версия, а не папина. Родительскую он, блядь, в игнор отправил.

class Animal:
    def make_sound(self):
        print("Издает какой-то звук")

class Dog(Animal):
    # Переопределяем метод родительского класса
    def make_sound(self):
        print("Гав! Гав!")

animal = Animal()
dog = Dog()

animal.make_sound() # Вывод: Издает какой-то звук
dog.make_sound()    # Вывод: Гав! Гав!

Видишь? animal мычит какую-то хуйню, а dog уже конкретно гавкает. Всё, родительский метод накрылся медным тазом для этого экземпляра.

А теперь важный момент, блядь! Часто же не хочется родительский метод полностью выпиливать. Хочется его расширить. Типа: "Папа, твою базовую логику я выполню, но потом ещё и от себя хуйни добавлю". Вот для этого и нужен super() — этакая волшебная палочка, чтобы позвать папину версию метода.

Смотри, вот реальная жизненная ситуация:

class Car:
    def __init__(self, brand):
        self.brand = brand
        print(f"Создан автомобиль марки {self.brand}.")

class ElectricCar(Car):
    def __init__(self, brand, battery_size):
        # Сначала вызываем __init__ родительского класса
        super().__init__(brand)  # Эй, пап, инициализируй бренд!
        # Затем добавляем свою логику
        self.battery_size = battery_size
        print(f"Это электромобиль с батареей {self.battery_size} кВт⋅ч.")

my_tesla = ElectricCar("Tesla", 100)

Что здесь происходит? Мы создаём электрокар. Если бы мы просто переопределили __init__ и не вызвали super(), то про бренд все забыли бы нахуй. А так — сначала выполнился папин __init__, который сохранил brand и напечатал первую строчку, а потом уже наш детский код добавил battery_size и вторую строчку. Красота, блядь!

И да, новость для педантов: с Python 3.12 можно использовать декоратор @typing.override. Это такой намёк и компилятору, и тебе самому: "Чувак, я тут намеренно переопределяю метод, если я ебнулся и имя метода опечатал — скажи мне, а то я, бля, сам не замечу!" Удобная штука, чтобы не выстрелить себе в ногу.