背景 - 我们可以自动初始化父类和子类!
这里有很多答案都说“这不是 Python 的方式,应该在子类中使用 super().__init__()
”。但问题并不是要求“Pythonic”的方式,而是将其他语言的预期行为与 Python 显然不同的行为进行比较。
MRO 文档非常漂亮且色彩缤纷,但它真的是一个 TLDR(太长不看)的情况,仍然不能完全回答问题,就像在这些类型的比较中经常发生的那样 - “按照 Python 的方式做,因为……”。
继承对象可以被后面声明的子类重载,一种模式建立在 @keyvanrm 的 (https://dev59.com/Zm865IYBdhLWcg3wfuwk#46943772) 答案上解决了这种情况:当我想要在调用类时 自动初始化 父类作为每个子类中没有显式调用 super().__init__()
的一部分时。
在我的情况下,新的团队成员可能会被要求使用一个样板模块模板(用于对我们的应用程序进行扩展而不触及核心应用程序源代码),我们希望使其尽可能简单易懂,无需了解底层机制 - 只需要知道并使用应用程序基本接口所提供的内容,这些接口都有很好的文档说明。
对于那些认为“显式比隐式更好”的人,我通常是同意的。然而,当来自许多其他流行语言的继承自动初始化时,这是预期的行为,并且如果可以利用它来扩展一些核心应用程序和其他工作,则非常有用。
这种技术甚至可以传递args/keyword args以进行初始化,这意味着几乎任何对象都可以推送到父级并由父类或其相关类使用。
例子:
class Parent:
def __init__(self, *args, **kwargs):
self.somevar = "test"
self.anothervar = "anothertest"
self._init(*args, **kwargs)
def _init(self, *args, **kwargs):
print("Parent class _init; ", self, args, kwargs)
def some_base_method(self):
print("some base method in Parent")
self.a_new_dict={}
class Child1(Parent):
def _init(self, *args, **kwargs):
print(f"Child1 class _init() overload; ",self, args, kwargs)
self.a_var_set_from_child = "This is a new var!"
class Child2(Parent):
def __init__(self, onevar, twovar, akeyword):
print(f"Child2 class __init__() overload; ", self)
self.some_base_method()
print(self.a_new_dict)
class Child3(Parent):
pass
print("\nRunning Parent()")
Parent()
Parent("a string", "something else", akeyword="a kwarg")
print("\nRunning Child1(), keep Parent.__init__(), overload surrogate Parent._init()")
Child1()
Child1("a string", "something else", akeyword="a kwarg")
print("\nRunning Child2(), overload Parent.__init__()")
Child2("a string", "something else", akeyword="a kwarg")
print("\nRunning Child3(), empty class, inherits everything")
Child3().some_base_method()
输出:
Running Parent()
Parent class _init; <__main__.Parent object at 0x7f84a721fdc0> () {}
Parent class _init; <__main__.Parent object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}
Running Child1(), keep Parent.__init__(), overload surrogate Parent._init()
Child1 class _init() overload; <__main__.Child1 object at 0x7f84a721fdc0> () {}
Child1 class _init() overload; <__main__.Child1 object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}
Running Child2(), overload Parent.__init__()
Child2 class __init__() overload; <__main__.Child2 object at 0x7f84a721fdc0>
some base method in Parent
{}
Running Child3(), empty class, inherits everything, access things set by other children
Parent class _init; <__main__.Child3 object at 0x7f84a721fdc0> () {}
some base method in Parent
正如大家所看到的,重载的定义取代了父类中声明的定义,但仍然可以被父类调用,从而允许我们模拟经典的隐式继承初始化行为,父类和子类都可以在不需要显式调用父类的init()的情况下进行初始化。
个人而言,我将代理_init()方法称为main()
,因为当我在C++和Python之间切换时,这对我来说是有意义的,因为它是一个将自动运行于Parent的任何子类(即最后一个声明的main()
定义)的函数。
__init__
方法,甚至可以自动搜索子类并对其进行装饰。 - Sergey Orshanskiysuper()
的调用。 - user26742873