为什么不会自动调用超类的 __init__ 方法?

186
为什么Python设计者决定子类的__init__()方法不会自动调用其超类的__init__()方法,与其他一些语言不同?Pythonic和推荐的惯用法是否真的像以下这样?
class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'

2
你可以编写一个装饰器来继承__init__方法,甚至可以自动搜索子类并对其进行装饰。 - Sergey Orshanskiy
2
@osa,听起来是个非常好的想法。您能否再详细描述一下装饰器部分? - Diansheng
1
@osa 是的,请进一步说明! - Charlie Parker
@Diansheng 我猜这只是一个对 super() 的调用。 - user26742873
@CharlieParker 我已经添加了一篇关于此事的答案。 - user26742873
显示剩余3条评论
11个回答

0

背景 - 我们可以自动初始化父类和子类!

这里有很多答案都说“这不是 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"

        #important part, call the init surrogate pass through args:
        self._init(*args, **kwargs)

    #important part, a placeholder init surrogate:
    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):
    # when omitted, the parent class's __init__() is run
    #def __init__(self):
    #    pass

    #overloading the parent class's  _init() surrogate
    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)

        #call some_base_method from parent
        self.some_base_method()

        #the parent's base method set a_new_dict
        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() # __init__() requires arguments
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()定义)的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接