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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3782827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:43:24  来源:igfitidea点击:

Why aren't superclass __init__ methods automatically invoked?

pythoninheritancesubclassdelegationsuperclass

提问by jrdioko

Why did the Python designers decide that subclasses' __init__()methods don't automatically call the __init__()methods of their superclasses, as in some other languages? Is the Pythonic and recommended idiom really like the following?

为什么 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'

采纳答案by Alex Martelli

The crucial distinction between Python's __init__and those other languages constructorsis that __init__is nota constructor: it's an initializer(the actual constructor(if any, but, see later;-) is __new__and works completely differently again). While constructingall superclasses (and, no doubt, doing so "before" you continue constructing downwards) is obviously part of saying you're constructinga subclass's instance, that is clearly not the case for initializing, since there are many use cases in which superclasses' initialization needs to be skipped, altered, controlled -- happening, if at all, "in the middle" of the subclass initialization, and so forth.

Python的之间至关重要的差别__init__及其它语言的构造是,__init__不是一个构造器:它是一个初始化(实际构造函数(如果有的话,但是,看到后来;-)是__new__和作品完全不同的一次)。在构建所有超类时(毫无疑问,在继续向下构建之前这样做)显然是在说您正在构建子类的实例的一部分,这显然不是初始化的情况,因为有很多用例需要跳过、更改、控制超类的初始化——如果有的话,发生在子类初始化的“中间”,等等。

Basically, super-class delegation of the initializer is not automatic in Python for exactly the same reasons such delegation is also not automatic for anyother methods -- and note that those "other languages" don't do automatic super-class delegation for any other method either... justfor the constructor (and if applicable, destructor), which, as I mentioned, is notwhat Python's __init__is. (Behavior of __new__is also quite peculiar, though really not directly related to your question, since __new__is such a peculiar constructor that it doesn't actually necessarily need to construct anything -- could perfectly well return an existing instance, or even a non-instance... clearly Python offers you a lotmore control of the mechanics than the "other languages" you have in mind, which alsoincludes having no automatic delegation in __new__itself!-).

基本上,初始化器的超类委托在 Python 中不是自动的,原因完全相同,这种委托对于任何其他方法也不是自动的——并注意那些“其他语言”不会为任何其他方法或者……用于构造函数(如果适用,还有析构函数),正如我所提到的,这不是Python 的__init__。(的行为__new__也很奇特,虽然实际上与您的问题没有直接关系,因为它__new__是如此奇特的构造函数,它实际上并不一定需要构造任何东西——完全可以返回一个现有的实例,甚至是一个非实例... 显然 Python 为您提供了很多与您想到的“其他语言”相比,对机制的控制更多,其中包括__new__本身没有自动委派!-)。

回答by Mike Axiak

"Explicit is better than implicit." It's the same reasoning that indicates we should explicitly write 'self'.

“显式优于隐式。” 同样的推理表明我们应该明确地写“自我”。

I think in in the end it is a benefit-- can you recite all of the rules Java has regarding calling superclasses' constructors?

我认为最终这是一个好处——你能背诵 Java 关于调用超类的构造函数的所有规则吗?

回答by John La Rooy

Often the subclass has extra parameters which can't be passed to the superclass.

通常子类具有无法传递给超类的额外参数。

回答by Glenn Maynard

I'm somewhat embarrassed when people parrot the "Zen of Python", as if it's a justification for anything. It's a design philosophy; particular design decisions can alwaysbe explained in more specific terms--and they must be, or else the "Zen of Python" becomes an excuse for doing anything.

当人们模仿“Python 之禅”时,我有些尴尬,好像这是为任何事情辩护的理由。这是一种设计理念;特定的设计决策总是可以用更具体的术语来解释——它们必须如此,否则“Python 之禅”就会成为做任何事情的借口。

The reason is simple: you don't necessarily construct a derived class in a way similar at all to how you construct the base class. You may have more parameters, fewer, they may be in a different order or not related at all.

原因很简单:您不一定以与构建基类的方式完全相似的方式构建派生类。您可能有更多的参数,也可能更少,它们的顺序可能不同或根本不相关。

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

This applies to all derived methods, not just __init__.

这适用于所有派生方法,而不仅仅是__init__.

回答by viraptor

Right now, we have a rather long page describing the method resolution order in case of multiple inheritance: http://www.python.org/download/releases/2.3/mro/

现在,我们有一个相当长的页面描述了多重继承情况下的方法解析顺序:http: //www.python.org/download/releases/2.3/mro/

If constructors were called automatically, you'd need another page of at least the same length explaining the order of that happening. That would be hell...

如果构造函数是自动调用的,则您需要至少相同长度的另一页来解释发生的顺序。那将是地狱...

回答by Kirk Strauser

Maybe __init__is the method that the subclass needs to override. Sometimes subclasses need the parent's function to run before they add class-specific code, and other times they need to set up instance variables before calling the parent's function. Since there's no way Python could possibly know when it would be most appropriate to call those functions, it shouldn't guess.

也许__init__是子类需要重写的方法。有时子类需要在添加特定于类的代码之前运行父类的函数,而有时它们需要在调用父类的函数之前设置实例变量。因为 Python 不可能知道什么时候调用这些函数最合适,所以它不应该猜测。

If those don't sway you, consider that __init__is Just Another Function. If the function in question were dostuffinstead, would you still want Python to automatically call the corresponding function in the parent class?

如果这些没有影响您,请考虑这__init__只是另一个功能。如果是这个函数dostuff,你还想让 Python 自动调用父类中的对应函数吗?

回答by flow

i believe the one very important consideration here is that with an automatic call to super.__init__(), you proscribe, by design, when that initialization method is called, and with what arguments. eschewing automatically calling it, and requiring the programmer to explicitly do that call, entails a lot of flexibility.

我相信这里有一个非常重要的考虑因素是,通过自动调用super.__init__(),您可以按照设计禁止调用该初始化方法的时间以及使用什么参数。避免自动调用它,并要求程序员明确地执行该调用,需要很大的灵活性。

after all, just because class B is derived from class A does not mean A.__init__()can or should be called with the same arguments as B.__init__(). making the call explicit means a programmer can have e.g. define B.__init__()with completely different parameters, do some computation with that data, call A.__init__()with arguments as appropriate for that method, and then do some postprocessing. this kind of flexibility would be awkward to attain if A.__init__()would be called from B.__init__()implicitly, either before B.__init__()executes or right after it.

毕竟,仅仅因为类 B 派生自类 A 并不意味着A.__init__()可以或应该使用与B.__init__(). 使调用显式意味着程序员可以B.__init__()使用完全不同的参数进行定义,使用该数据进行一些计算,A.__init__()使用适合该方法的参数进行调用,然后进行一些后处理。如果在执行之前或之后A.__init__()B.__init__()隐式调用,这种灵活性将很难实现B.__init__()

回答by Ben

Java and C++ requirethat a base class constructor is called because of memory layout.

由于内存布局,Java 和 C++要求调用基类构造函数。

If you have a class BaseClasswith a member field1, and you create a new class SubClassthat adds a member field2, then an instance of SubClasscontains space for field1and field2. You need a constructor of BaseClassto fill in field1, unless you require all inheriting classes to repeat BaseClass's initialization in their own constructors. And if field1is private, then inheriting classes can'tinitialise field1.

如果您有一个BaseClass包含成员的类field1,并且您创建了一个SubClass添加成员的新类field2,则 的实例SubClass包含field1和 的空间field2。您需要一个 的构造函数BaseClass来填充field1,除非您要求所有继承类BaseClass在它们自己的构造函数中重复的初始化。如果field1是私有的,则继承类不能初始化field1

Python is not Java or C++. All instances of all user-defined classes have the same 'shape'. They're basically just dictionaries in which attributes can be inserted. Before any initialisation has been done, all instances of all user-defined classes are almost exactly the same; they're just places to store attributes that aren't storing any yet.

Python 不是 Java 或 C++。所有用户定义类的所有实例都具有相同的“形状”。它们基本上只是可​​以插入属性的字典。在任何初始化完成之前,所有用户定义类的所有实例几乎完全相同;它们只是存储尚未存储的属性的地方。

So it makes perfect sense for a Python subclass not to call its base class constructor. It could just add the attributes itself if it wanted to. There's no space reserved for a given number of fields for each class in the hierarchy, and there's no difference between an attribute added by code from a BaseClassmethod and an attribute added by code from a SubClassmethod.

因此,Python 子类不调用其基类构造函数是完全合理的。如果它愿意,它可以只添加属性本身。对于层次结构中的每个类,没有为给定数量的字段保留空间,并且由BaseClass方法的代码添加的属性与由方法的代码添加的属性之间没有区别SubClass

If, as is common, SubClassactually does want to have all of BaseClass's invariants set up before it goes on to do its own customisation, then yes you can just call BaseClass.__init__()(or use super, but that's complicated and has its own problems sometimes). But you don't have to. And you can do it before, or after, or with different arguments. Hell, if you wanted you could call the BaseClass.__init__from another method entirely than __init__; maybe you have some bizarre lazy initialization thing going.

如果通常情况下SubClass确实希望BaseClass在继续进行自己的自定义之前设置所有的不变量,那么是的,您可以直接调用BaseClass.__init__()(或使用super,但这很复杂,有时也有其自身的问题)。但你没有必要。你可以在之前、之后或使用不同的参数来做。地狱,如果你愿意,你可以BaseClass.__init__完全从另一个方法调用__init__; 也许你有一些奇怪的懒惰初始化的事情。

Python achieves this flexibility by keeping things simple. You initialise objects by writing an __init__method that sets attributes on self. That's it. It behaves exactly like a method, because it is exactly a method. There are no other strange and unintuitive rules about things having to be done first, or things that will automatically happen if you don't do other things. The only purpose it needs to serve is to be a hook to execute during object initialisation to set initial attribute values, and it does just that. If you want it to do something else, you explicitly write that in your code.

Python 通过让事情变得简单来实现这种灵活性。您可以通过编写在__init__上设置属性的方法来初始化对象self。就是这样。它的行为与方法完全一样,因为它完全是一种方法。关于必须先做的事情,或者如果你不做其他事情就会自动发生的事情,没有其他奇怪和不直观的规则。它需要服务的唯一目的是在对象初始化期间执行以设置初始属性值的钩子,它就是这样做的。如果你想让它做别的事情,你可以在代码中明确地写出来。

回答by Keyvanrm

To avoid confusion it is useful to know that you can invoke the base_class __init__()method if the child_class does not have an __init__()class.

为了避免混淆,知道__init__()如果 child_class 没有__init__()类,您可以调用 base_class方法是有用的。

Example:

例子:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

In fact the MRO in python will look for __init__()in the parent class when can not find it in the children class. You need to invoke the parent class constructor directly if you have already an __init__()method in the children class.

实际上python中的MRO在__init__()子类中找不到时会在父类中查找。如果子类中已有__init__()方法,则需要直接调用父类构造函数。

For example the following code will return an error: class parent: def init(self, a=1, b=0): self.a = a self.b = b

例如下面的代码会返回一个错误: class parent: def init(self, a=1, b=0): self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.