Python-2.x 中的 super() 是否损坏?
人们经常说 super
应该是 在 Python 2 中避免。我在 Python 2 中使用 super
时发现除非我提供所有内容,否则它永远不会按照我期望的方式行事参数,例如示例:
super(ThisClass, self).some_func(*args, **kwargs)
在我看来,这违背了使用 super() 的目的,它既不比 TheBaseClass.some_func(self, *args, ** 更简洁,也不更好) kwargs)
。对于大多数用途来说,方法解析顺序是一个遥远的童话。
- 除了 2.7 是最后一个主要版本这一事实 到 Python 2,为什么 super 在 Python 2 中仍然损坏?
- Python 3 的 super 如何以及为何发生变化?有什么注意事项吗?
- 我应该何时以及为何使用
super
?
It's often stated that super
should be avoided in Python 2. I've found in my use of super
in Python 2 that it never acts the way I expect unless I provide all arguments such as the example:
super(ThisClass, self).some_func(*args, **kwargs)
It seems to me this defeats the purpose of using super()
, it's neither more concise, or much better than TheBaseClass.some_func(self, *args, **kwargs)
. For most purposes method resolution order is a distant fairy tale.
- Other than the fact that 2.7 is the last major release to Python 2, why does super remain broken in Python 2?
- How and why has Python 3's super changed? Are there any caveats?
- When and why should I use
super
going forward?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
super() 并没有被破坏——它只是不应该被视为调用基类方法的标准方法。 Python 3.x 中这一点没有改变。唯一改变的是,在标准情况下,您不需要传递参数
self, cls
,即self
是当前函数的第一个参数,并且>cls
是当前正在定义的类。关于你的问题何时实际使用
super()
,我的答案是:几乎没有。我个人尝试避免那种会使super()
有用的多重继承。编辑:我曾经遇到过的现实生活中的例子:我有一些类定义了
run()
方法,其中一些具有基类。我使用 super() 来调用继承的构造函数——我认为这并不重要,因为我只使用单继承:想象一下有几个这样的类,它们都具有单独的构造函数原型,并且所有与
run()
具有相同的接口。现在我想向所有这些类添加一些附加功能,例如日志记录。附加功能需要在所有这些类上定义附加方法,例如
info()
。我不想侵入原始类,而是定义从原始类继承的第二组类,添加info()
方法并从提供实际日志记录的混合继承。现在,我无法再在构造函数中使用super()
,因此我使用了直接调用:这里事情停止了。基类构造函数中的
super()
调用突然调用了Logger.__init__()
,而BLogged
对此无能为力。实际上,除了删除B
本身中的super()
调用之外,没有其他方法可以实现此目的。[另一个编辑:从这里和其他答案下面的所有评论来看,我似乎没有表达我的观点。以下是如何使用
super()
使该代码工作:将其与显式超类调用的使用进行比较。您决定您喜欢哪个版本。]
这个故事和类似的故事就是为什么我认为
super()
不应该被视为调用基类方法的标准方式。这并不意味着super()
已损坏。super()
is not broken -- it just should not be considered the standard way of calling a method of the base class. This did not change with Python 3.x. The only thing that changed is that you don't need to pass the argumentsself, cls
in the standard case thatself
is the first parameter of the current function andcls
is the class currently being defined.Regarding your question when to actually use
super()
, my answer would be: hardly ever. I personally try to avoid the kind of multiple inheritance that would makesuper()
useful.Edit: An example from real life that I once ran into: I had some classes defining a
run()
method, some of which had base classes. I usedsuper()
to call the inherited constructors -- I did not think it mattered because I was using single inheritance only:Just imagine there were several of these classes, all with individual constructor prototypes, and all with the same interface to
run()
.Now I wanted to add some additional functionality to all of these classes, say logging. The additional functionality required an additional method to be defined on all these classes, say
info()
. I did not want to invade the original classes, but rather define a second set of classes inheriting from the original ones, adding theinfo()
method and inheriting from a mix-in providing the actual logging. Now, I could not usesuper()
in the constructor any more, so I used direct calls:Here things stop working. The
super()
call in the base class constructor suddenly callsLogger.__init__()
, andBLogged
can't do anything about it. There is actually no way to make this work, except for removing thesuper()
call inB
itself.[Another Edit: I don't seem to have made my point, judging from all the comments here and below the other answers. Here is how to make this code work using
super()
:Compare this with the use of explicit superclass calls. You decide which version you prefer.]
This and similar stories are why I think that
super()
should not be considered the standard way of calling methods of the base class. It does not meansuper()
is broken.super()
在 Python 2 或 Python 3 中并没有被破坏。让我们考虑一下博客文章中的论点:
好吧,你可能同意或不同意这一点,这是相当主观的。那它应该叫什么呢? super() 是直接调用超类的替代品,所以这个名字对我来说似乎很好。它不会直接调用超类,因为如果这就是它所做的一切,那就毫无意义,因为无论如何你都可以这样做。好吧,诚然,这可能并不明显,但是需要
super()
的情况通常并不明显。如果你需要它,你正在做一些相当复杂的多重继承。这不会很明显。 (或者您正在做一个简单的混合,在这种情况下,即使您没有阅读文档,它也会非常明显并且按照您的预期运行)。如果您可以直接调用超类,那么您最终可能会这样做。这是简单且直观的方法。
super()
仅在不起作用时才起作用。是的,因为它旨在解决这样做的问题。当且仅当您确切知道该超类是什么时,您才可以直接调用该超类。例如,当您的类层次结构非常混乱以至于您实际上正在合并两个分支(这是使用
super()
的所有示例中的典型示例)时,您就不需要这样做。因此,只要类层次结构中的每个类都有明确定义的位置,直接调用超类就可以了。如果不这样做,那么它就不起作用,在这种情况下,您必须使用
super()
来代替。这就是 super() 的要点,它根据 MRO 计算出“下一个超类”是什么,而无需您明确指定它,因为您不能总是这样做,因为您不这样做并不总是知道它是什么,例如在使用 mixin 时。呃。好的?
super()
不会调用您的超类。是的,你说过。
super()
和直接调用。是的,你也这么说。
所以,有两个理由反对: 1. 这个名字不好。 2. 您必须始终如一地使用它。
这并不意味着它被“破坏”或应该“避免”。
super()
is not broken, in Python 2 or Python 3.Let's consider the arguments from the blog post:
OK, you may agree or disagree on that, it's pretty subjective. What should it have been called then?
super()
is a replacement for calling the superclass directly, so the name seems fine to me. It does NOT call the superclass directly, because if that was all it did, it would be pointless, as you could do that anyway. OK, admittedly, that may not be obvious, but the cases where you needsuper()
are generally not obvious. If you need it, you are doing some pretty hairy multiple inheritance. It's not going to be obvious. (Or you are doing a simple mixin, in which case it will be pretty obvious and behave as you expect even if you didn't read the docs).If you can call the superclass directly, that's probably what you'll end up doing. That's the easy and intuitive way of doing it.
super()
only comes into play when that doesn't work.Yes, because it's designed to solve a problem with doing that. You can call the superclass directly if, and only if, you know exactly what class that is. Which you don't for mixins, for example, or when your class hierarchy is so messed up that you actually are merging two branches (which is the typical example in all examples of using
super()
).So as long as every class in your class hierarchy has a well defined place, calling the superclass directly works. If you don't, then it does not work, and in that case you must use
super()
instead. That's the point ofsuper()
that it figures out what the "next superclass" is according to the MRO, without you explicitly having to specify it, because you can't always do that because you don't always know what it is, for example when using mixins.Eh. OK?
super()
doesn't call your superclass.Yeah, you said that.
super()
and direct calling.Yeah, you said that too.
So, there is two arguments against it: 1. The name is bad. 2. You have to use it consistently.
That does not translate to it being "broken" or that it should be "avoided".
您似乎在您的帖子中暗示这
不是无限递归。是的,super 会更正确。
另外,是的,您需要将所有参数传递给
super()
。这有点像抱怨max()
无法按预期工作,除非您将要检查的所有数字传递给它。然而,在 3.x 中,需要的参数更少:您可以执行
super().foo(*args, **kwargs)
而不是super(ThisClass, self).foo(* args,**kwargs)
。不管怎样,我不确定在什么情况下应该避免使用 super 。当涉及到 MI 时,它的行为只是“奇怪”,而当涉及到 MI 时,
super()
基本上就是你的 只希望有一个正确的解决方案。在单继承中,它只是比 SuperClass.foo(self, *args, **kwargs) 稍微冗长一些,并且没有什么不同。我认为我同意 Sven 的观点,即这种 MI 值得避免,但我不同意
super
值得避免。如果您的类应该被继承,super
将为您的类的用户提供让 MI 工作的希望(如果他们在这方面很奇怪的话),因此它使您的类更可用。You seem to imply in your post that
is not an infinite recursion. It is, and super would be more correct.
Also, yes, you are required to pass all arguments to
super()
. This is a bit like complaining thatmax()
doesn't work like expected unless you pass it all the numbers you want to check.In 3.x, however, fewer arguments are needed: you can do
super().foo(*args, **kwargs)
instead ofsuper(ThisClass, self).foo(*args, **kwargs)
.Anyway, I'm unsure as to any situations when super should be avoided. Its behavior is only "weird" when MI is involved, and when MI is involved,
super()
is basically your only hope for a correct solution. In Single-Inheritance it's just slightly wordier thanSuperClass.foo(self, *args, **kwargs)
, and does nothing different.I think I agree with Sven that this sort of MI is worth avoiding, but I don't agree that
super
is worth avoiding. If your class is supposed to be inherited,super
offers users of your class hope of getting MI to work, if they're weird in that way, so it makes your class more usable.您阅读了链接的文章吗?它并没有得出应该避免
super
的结论,但您在使用它时应该警惕它的注意事项。文章总结了这些注意事项,但我不同意他们的建议。本文的要点是,多重继承可能会变得混乱,而
super
并没有像作者想要的那样有帮助。然而,在没有super
的情况下进行多重继承通常会更加复杂。如果您不进行多重继承,
super
为您提供了一个优势,即从您的类继承的任何人都可以添加简单的 mixin,并且它们的__init__
将被正确调用。只需记住始终调用超类的 __init__ ,即使您是从object
继承的,并传递所有剩余的参数 (*a
> 和**kw
) 。当您从父类调用其他方法时,也使用super
,但这次使用您已经知道的正确签名(即确保它们在所有类中具有相同的签名)。如果您正在进行多重继承,则必须比这更深入地挖掘,并且可能更仔细地重新阅读同一篇文章以了解注意事项。而且只有在多重继承期间,您可能会出现显式调用父级可能比 super 更好的情况,但如果没有特定的场景,没有人可以告诉您是否 super应该使用或不使用。
Python 3.x 中
super
的唯一变化是您不需要显式地将当前类和self
传递给它。这使得 super 更有吸引力,因为使用它意味着父类或当前类都不会进行硬编码。Did you read the article that you link it? It doesn't conclude that
super
should be avoided but that you should be wary of its caveats when using it. These caveats are summarized by the article, though I would disagree with their suggestions.The main point of the article is that multiple inheritance can get messy, and
super
doesn't help as much as the author would want. However doing multiple inheritance withoutsuper
is often even more complicated.If you're not doing multiple inheritance,
super
gives you the advantage that anyone inheriting from your class can add simple mixins and their__init__
would be properly called. Just remember to always call the__init__
of the superclass, even when you're inheriting fromobject
, and to pass all the remaining arguments (*a
and**kw
) to it. When you're calling other methods from the parent class also usesuper
, but this time use their proper signature that you already know (i.e. ensure that they have the same signature in all classes).If you're doing multiple inheritance you'd have to dig deeper than that, and probably re-read the same article more carefully to be aware of the caveats. And it's also only during multiple inheritance when you might a situation where an explicit call to the parent might be better than
super
, but without a specific scenario nobody can tell you whethersuper
should be used or not.The only change in
super
in Python 3.x is that you don't need to explicitly pass the current class andself
to it. This makessuper
more attractive, because using it would mean no hardcoding of either the parent class or the current class.@Sven Marnach:
您的示例的问题在于您在博客中使用
super()
混合显式超类调用B.__init__
和Logger.__init__
在 B 中。那是行不通的。要么使用所有显式超类调用,要么在所有类上使用super()
。当您使用 super() 时,您需要在所有涉及的类上使用它,包括 AI think。另外,在您的示例中,我认为您可以在所有类中使用显式超类调用,即在类 B 中使用A.__init__
。当没有钻石继承时,我认为 super() 没有太大优势。然而,问题是,您事先并不知道将来是否会进入任何钻石继承,因此在这种情况下,无论如何使用 super() 都是明智的(但随后使用始终如一)。否则,您最终将不得不稍后更改所有课程或遇到问题。
@Sven Marnach:
The problem with your example is that you mix explicit superclass calls
B.__init__
andLogger.__init__
in Blogged withsuper()
in B. That won't work. Either you use all explicit superclass calls or usesuper()
on all classes. When you usesuper()
you need to use it on all classes involved, including A I think. Also in your example I think you could use explicit superclass calls in all classes, i.e useA.__init__
in class B.When there is no diamond inheritance I think super() doesn't have much advantage. The problem is, however, that you don't know in advance if you will get into any diamond inheritance in the future so in that case it would be wise to use
super()
anyway (but then use it consistently). Otherwise you would end up having to change all classes at a later time or run into problems.