Python多重继承中调用双亲的构造函数(一般情况)

Kar*_*ski 5 python inheritance super diamond-problem

我试图弄清楚Python中的多重继承,但我找到的所有文章都仅限于简单的情况。让我们考虑以下示例:

class Vehicle:
    def __init__(self, name: str) -> None:
        self.name = name
        print(f'Creating a Vehicle: {name}')

    def __del__(self):
        print(f'Deleting a Vehicle: {self.name}')


class Car(Vehicle):
    def __init__(self, name: str, n_wheels: int) -> None:
        super().__init__(name)
        self.wheels = n_wheels
        print(f'Creating a Car: {name}')

    def __del__(self):
        print(f'Deleting a Car: {self.name}')


class Boat(Vehicle):
    def __init__(self, name: str, n_props: int) -> None:
        super().__init__(name)
        self.propellers = n_props
        print(f'Creating a Boat: {name}')

    def __del__(self):
        print(f'Deleting a Boat: {self.name}')


class Amfibii(Car, Boat):
    def __init__(self, name: str, n_wheels: int, n_props: int) -> None:
        Car.__init__(self, name, n_wheels)
        Boat.__init__(self, name, n_props)
        print(f'Creating an Amfibii: {name}')

    def __del__(self):
        print(f'Deleting an Amfibii: {self.name}')


my_vehicle = Amfibii('Mazda', 4, 2)
Run Code Online (Sandbox Code Playgroud)

我想了解调用构造函数和析构函数的顺序,以及“super”关键字的正确和一般用法。在上面的示例中,我收到以下错误:

super().__init__(name) 类型错误:Boat.__init__()缺少 1 个必需的位置参数:'n_props'

我应该如何正确调用具有不同构造函数参数集的父级构造函数?

Kar*_*ski 1

感谢评论和下面的文章,我明白了。https://rhettinger.wordpress.com/2011/05/26/super-considered-super/

  1. 关于构造函数的参数,它们应该被命名。通过这种方式,每个级别都会剥离它需要的内容并将其传递到下一个级别super()

  2. 每个班级super().__init__()只调用一次。MRO 顺序中的以下类负责继续初始化。

更新后的示例:

class Vehicle:
    def __init__(self, name: str) -> None:
        self.name = name
        print(f'Creating a Vehicle: {name}')

    def go(self):
        print('Somehow moving...')
        assert not hasattr(super(), 'go')


class Car(Vehicle):
    def __init__(self, n_wheels: int,  **kwargs) -> None:
        super().__init__(**kwargs)
        self.wheels = n_wheels
        print(f'Creating a Car: {self.name}')

    def go(self):
        print('Riding...')
        super().go()


class Boat(Vehicle):
    def __init__(self, n_props, **kwargs) -> None:
        super().__init__(**kwargs)
        self.propellers = n_props
        print(f'Creating a Boat: {self.name}')

    def go(self):
        print('Swimming...')
        super().go()


class Amfibii(Car, Boat):
    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        print(f'Creating an Amfibii: {self.name}')

    def go(self):
        print('Riding or swimming...')
        super().go()


my_vehicle = Amfibii(name='Mazda', n_wheels=4, n_props=2)
my_vehicle.go()
Run Code Online (Sandbox Code Playgroud)

这有点麻烦(温和地说),不是吗?

编辑:我根据评论更新了代码。

  • 不相关的是,您几乎可以肯定无需使用“__del__”方法。`__del__` 并不完全对应于您可能熟悉的 C++ 或 Java 中的析构函数,并且仅在某些罕见的情况下才需要。 (3认同)
  • 我也会从“__init__”的每个定义中省略“*args”。链接文章建议的一件事是您仅使用关键字参数。请注意,例如,您可以为“Boat.__init__”编写“def __init__(self, *, n_props, **kwargs):”,不仅不会接受其他位置参数,而且“n_props”本身也必须是用关键字参数指定。 (2认同)