Передача определённых параметров из инициализатора родительского класса

Есть вполне себе простая задачка по подсчету площади в дочерних классах "столов"

import math


class Desk:
    def __init__(self, width: int = 0, length: int = 0, radius: int = 0):
        self._width = width
        self._length = length
        self._radius = radius 


class RectDesk(Desk):
    def area(self):
        return self._width * self._length


class RoundDesk(Desk):
    def area(self):
        return round(math.pi * self._radius**2, 2)


rect_desk = RectDesk(20, 10)
print(rect_desk.__dict__)
print(rect_desk.area())
rect_desk2 = RectDesk(20, 20)
print(rect_desk2.__dict__)
print(rect_desk2.area())
round_desk = RoundDesk(radius=20)
print(round_desk.__dict__)
print(round_desk.area())

Однако у меня возник вопрос, можно ли каким-то образом передавать не все параметры из инициализатора родительского класса? Очевидно, что для подсчета "Прямоугольного стола" мне не нужен радиус, однако все равно он передается.

При переопределении инициализатора, все равно просит радиус

class Desk:
    def __init__(self, width, length, radius):
        self._width = width
        self._length = length
        self._radius = radius

    def area(self):
        raise NotImplementedError("In child class must be area calculating method")


class RectDesk(Desk):
    def __init__(self, width, length):
        super().__init__(width, length)
Traceback (most recent call last):
  File "B:\python\hws\Desks.py", line 27, in <module>
    rect_desk = RectDesk(20, 10)
  File "B:\python\hws\Desks.py", line 16, in __init__
    super().__init__(width, length)
TypeError: Desk.__init__() missing 1 required positional argument: 'radius'

Ответы (2 шт):

Автор решения: Павел

Вы не совсем верно понимаете наследование, дочерние классы должны расширять базовый класс, а не сжимать его.

Советую почитать про базовые принципы ООП и SOLID.

В данном случае у вас общее у всех объектов типа стол то, что у всех таких объектов можно посчитать площадь. Других сходств нет, входные параметры сильно отличаются в зависимости от типа, поэтому гораздо логичнее реализовать интерфейс типа Desk с обязательным для реализации методом area (и вы кстати сами к этому пришли во второй вашей попытке, где выдаете NotImplementedError при расчете площади в базовом классе).

В Python в качестве альтернативы интерфейсу можно использовать абстрактный класс ABC.

Правильная архитектура на мой взгляд должна выглядеть как-то так:

import math
from abc import ABC, abstractmethod


class Desk(ABC):
    @abstractmethod
    def area(self):
        pass


class RectDesk(Desk):
    def __init__(self, width, length):
        self._width = width
        self._length = length

    def area(self):
        return self._width * self._length


class RoundDesk(Desk):
    def __init__(self, radius):
        self._radius = radius

    def area(self):
        return round(math.pi * self._radius ** 2, 2)


rect_desk = RectDesk(20, 10)
print(rect_desk.__dict__)
print(rect_desk.area())

round_desk = RoundDesk(20)
print(round_desk.__dict__)
print(round_desk.area())
→ Ссылка
Автор решения: Павел

В предыдущем ответе верно сказали - это не очень хорошая идея, конструировать классы таким образом. Однако тебе может понадобиться перегрузить класс из чужой библиотеки, убрав лишние параметры, поэтому твой вопрос вполне уместен.

Вариант 1: Если готов править родительский класс

class Desk:
    def __init__(self, width=None, length=None, radius=None):
        if not width is None:
            self._width = width
        if not length is None:
            self._length = length
        if not radius is None:
            self._radius = radius

class RectDesk(Desk):
    def __init__(self, width, length):
        super().__init__(width, length)

Здесь будут инициализированы только те переменные, которые были переданы в super().init

Вариант 2: Если не готов править родительский класс

class RectDesk(Desk):
    def __init__(self, width, length):
        super().__init__(width, length, None)

        #Если хочется, чтобы таких параметров у класса не было совсем, то можно их удалить:
        del self.radius
→ Ссылка