Python-2.x 中的 super() 是否损坏?

发布于 2025-01-09 12:02:34 字数 834 浏览 0 评论 0原文

人们经常说 super 应该是 在 Python 2 中避免。我在 Python 2 中使用 super 时发现除非我提供所有内容,否则它永远不会按照我期望的方式行事参数,例如示例:

super(ThisClass, self).some_func(*args, **kwargs)

在我看来,这违背了使用 super() 的目的,它既不比 TheBaseClass.some_func(self, *args, ** 更简洁,也不更好) kwargs)。对于大多数用途来说,方法解析顺序是一个遥远的童话。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

焚却相思 2025-01-16 12:02:34

super() 并没有被破坏——它只是不应该被视为调用基类方法的标准方法。 Python 3.x 中这一点没有改变。唯一改变的是,在标准情况下,您不需要传递参数 self, cls,即 self 是当前函数的第一个参数,并且 >cls 是当前正在定义的类。

关于你的问题何时实际使用 super(),我的答案是:几乎没有。我个人尝试避免那种会使 super() 有用的多重继承。

编辑:我曾经遇到过的现实生活中的例子:我有一些类定义了 run() 方法,其中一些具有基类。我使用 super() 来调用继承的构造函数——我认为这并不重要,因为我只使用单继承:

class A(object):
    def __init__(self, i):
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, i, j):
        super(B, self).__init__(i)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

想象一下有几个这样的类,它们都具有单独的构造函数原型,并且所有与 run() 具有相同的接口。

现在我想向所有这些类添加一些附加功能,例如日志记录。附加功能需要在所有这些类上定义附加方法,例如 info()。我不想侵入原始类,而是定义从原始类继承的第二组类,添加 info() 方法并从提供实际日志记录的混合继承。现在,我无法再在构造函数中使用 super(),因此我使用了直接调用:

class Logger(object):
    def __init__(self, name):
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, i, j):
        B.__init__(self, i, j)
        Logger.__init__("B")
    def info(self):
        return 42

这里事情停止了。基类构造函数中的 super() 调用突然调用了 Logger.__init__(),而 BLogged 对此无能为力。实际上,除了删除 B 本身中的 super() 调用之外,没有其他方法可以实现此目的。

[另一个编辑:从这里和其他答案下面的所有评论来看,我似乎没有表达我的观点。以下是如何使用 super() 使该代码工作:

class A(object):
    def __init__(self, i, **kwargs):
        super(A, self).__init__(**kwargs)
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, j, **kwargs):
        super(B, self).__init__(**kwargs)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

class Logger(object):
    def __init__(self, name, **kwargs):
        super(Logger,self).__init__(**kwargs)
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, **kwargs):
        super(BLogged, self).__init__(name="B", **kwargs)
    def info(self):
        return 42

b = BLogged(i=3, j=4)

将其与显式超类调用的使用进行比较。您决定您喜欢哪个版本。]

这个故事和类似的故事就是为什么我认为 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 arguments self, cls in the standard case that self is the first parameter of the current function and cls 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 make super() 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 used super() to call the inherited constructors -- I did not think it mattered because I was using single inheritance only:

class A(object):
    def __init__(self, i):
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, i, j):
        super(B, self).__init__(i)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

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 the info() method and inheriting from a mix-in providing the actual logging. Now, I could not use super() in the constructor any more, so I used direct calls:

class Logger(object):
    def __init__(self, name):
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, i, j):
        B.__init__(self, i, j)
        Logger.__init__("B")
    def info(self):
        return 42

Here things stop working. The super() call in the base class constructor suddenly calls Logger.__init__(), and BLogged can't do anything about it. There is actually no way to make this work, except for removing the super() call in B 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():

class A(object):
    def __init__(self, i, **kwargs):
        super(A, self).__init__(**kwargs)
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, j, **kwargs):
        super(B, self).__init__(**kwargs)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

class Logger(object):
    def __init__(self, name, **kwargs):
        super(Logger,self).__init__(**kwargs)
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, **kwargs):
        super(BLogged, self).__init__(name="B", **kwargs)
    def info(self):
        return 42

b = BLogged(i=3, j=4)

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 mean super() is broken.

幼儿园老大 2025-01-16 12:02:34

super() 在 Python 2 或 Python 3 中并没有被破坏。

让我们考虑一下博客文章中的论点:

  • 它的作用并不像听起来那样。

好吧,你可能同意或不同意这一点,这是相当主观的。那它应该叫什么呢? super() 是直接调用超类的替代品,所以这个名字对我来说似乎很好。它不会直接调用超类,因为如果这就是它所做的一切,那就毫无意义,因为无论如何你都可以这样做。好吧,诚然,这可能并不明显,但是需要 super() 的情况通常并不明显。如果你需要它,你正在做一些相当复杂的多重继承。这不会很明显。 (或者您正在做一个简单的混合,在这种情况下,即使您没有阅读文档,它也会非常明显并且按照您的预期运行)。

如果您可以直接调用超类,那么您最终可能会这样做。这是简单且直观的方法。 super() 仅在不起作用时才起作用。

  • 它与直接调用超类并不能很好地配合。

是的,因为它旨在解决这样做的问题。当且仅当您确切知道该超类是什么时,您才可以直接调用该超类。例如,当您的类层次结构非常混乱以至于您实际上正在合并两个分支(这是使用 super() 的所有示例中的典型示例)时,您就不需要这样做。

因此,只要类层次结构中的每个类都有明确定义的位置,直接调用超类就可以了。如果不这样做,那么它就不起作用,在这种情况下,您必须使用 super() 来代替。这就是 super() 的要点,它根据 MRO 计算出“下一个超类”是什么,而无需您明确指定它,因为您不能总是这样做,因为您不这样做并不总是知道它是什么,例如在使用 mixin 时。

  • 完全不同的编程语言 Dylan(一种 lisp 的东西)以另一种无法在 Python 中使用的方式解决了这个问题,因为它非常不同。

呃。好的?

  • super() 不会调用您的超类。

是的,你说过。

  • 不要混合使用 super() 和直接调用。

是的,你也这么说。

所以,有两个理由反对: 1. 这个名字不好。 2. 您必须始终如一地使用它。

这并不意味着它被“破坏”或应该“避免”。

super() is not broken, in Python 2 or Python 3.

Let's consider the arguments from the blog post:

  • It doesn't do what it sounds like it does.

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 need super() 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.

  • It doesn't mesh well with calling the superclass directly.

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 of super() 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.

  • The completely different programming language Dylan, a sort of lisp-thingy, solves this in another way that can't be used in Python because it's very different.

Eh. OK?

  • super() doesn't call your superclass.

Yeah, you said that.

  • Don't mix 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".

温折酒 2025-01-16 12:02:34

您似乎在您的帖子中暗示这

def some_func(self, *args, **kwargs):
    self.__class__.some_func(self, *args, **kwargs)

不是无限递归。是的,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

def some_func(self, *args, **kwargs):
    self.__class__.some_func(self, *args, **kwargs)

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 that max() 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 of super(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 than SuperClass.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.

倾城泪 2025-01-16 12:02:34

您阅读了链接的文章吗?它并没有得出应该避免 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 without super 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 from object, and to pass all the remaining arguments (*a and **kw) to it. When you're calling other methods from the parent class also use super, 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 whether super 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 and self to it. This makes super more attractive, because using it would mean no hardcoding of either the parent class or the current class.

蛮可爱 2025-01-16 12:02:34

@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__ and Logger.__init__ in Blogged with super() in B. That won't work. Either you use all explicit superclass calls or use super() on all classes. When you use super() 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 use A.__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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文