在Python-2.x中是否打破了super()? [关闭]

问题描述 投票:39回答:5

经常声明super应该是Python 2中的avoided。我在Python 2中使用super时发现,除非我提供所有参数,例如示例,否则它永远不会按照我的预期行事:

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

在我看来,这打败了使用super()的目的,它既不简洁,也不比TheBaseClass.some_func(self, *args, **kwargs)好多了。在大多数情况下,方法解析顺序是一个遥远的童话故事。

python python-3.x multiple-inheritance super python-2.x
5个回答
42
投票

super()没有被破坏 - 它不应该被认为是调用基类方法的标准方法。这与Python 3.x没有变化。唯一改变的是你不需要在标准情况下传递参数self, clsself是当前函数的第一个参数,而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对此无能为力。实际上没有办法使这项工作,除了删除super()本身的B调用。

[另一个编辑:我似乎没有提出我的观点,从这里和其他答案的所有评论来判断。以下是如何使用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()被打破了。


31
投票

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

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

  • 它听起来并不像它那样做。

好的,你可能同意或不同意,这是非常主观的。应该怎么称呼呢? super()是直接调用超类的替代品,所以这个名字对我来说似乎很好。它不会直接调用超类,因为如果这就是它所做的全部,那将毫无意义,因为无论如何你都可以这样做。不可否认,这可能并不明显,但是你需要super()的情况通常并不明显。如果你需要它,你正在做一些非常多毛的多重继承。这不会很明显。 (或者你正在做一个简单的mixin,在这种情况下,即使你没有阅读文档,它也会非常明显并且表现得如你所愿)。

如果你可以直接调用超类,那可能就是你最终会做的事情。这是一种简单直观的方法。 super()只有在不起作用时才会发挥作用。

  • 它与直接调用超类不匹配。

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

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

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

呃。好吗?

  • super()不会打电话给你的超级班。

是的,你这么说。

  • 不要混合super()和直接呼叫。

是的,你也说过。

因此,有两个反对它的论点:1。名称不好。你必须始终如一地使用它。

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


6
投票

你似乎暗示你的帖子

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

不是无限递归。它是,超级更正确。

此外,是的,您需要将所有参数传递给super()。这有点像抱怨max()不能像预期的那样工作,除非你传递所有想要检查的数字。

然而,在3.x中,需要的参数较少:你可以做super().foo(*args, **kwargs)而不是super(ThisClass, self).foo(*args, **kwargs)


无论如何,我不确定应该避免超级的任何情况。当MI涉及时,它的行为只是“奇怪”,当涉及MI时,super()基本上是你的only hope正确的解决方案。在单继承中,它只比SuperClass.foo(self, *args, **kwargs)略显冗长,并没有什么不同。

我认为我同意Sven认为这种MI是值得避免的,但我不同意super值得避免。如果您的课程应该被继承,super为您的班级用户提供希望让MI工作,如果他们以这种方式很奇怪,那么它会使您的课程更有用。


3
投票

你读过你链接它的文章了吗?它没有得出结论,应该避免使用super,但是在使用它时你应该警惕它的警告。文章总结了这些警告,但我不同意他们的建议。

这篇文章的主要观点是多重继承可能会变得混乱,而super并没有像作者想要的那样多。但是,在没有super的情况下进行多重继承通常会更加复杂。

如果你没有做多重继承,super给你的优势是任何从你的类继承的人都可以添加简单的mixins,并且可以正确调用它们的__init__。只记得总是调用超类的__init__,即使你继承qa​​zxswpoi,并将所有剩余的参数(object*a)传递给它。当你从父类调用其他方法时也使用**kw,但这次使用你已经知道的正确签名(即确保它们在所有类中都有相同的签名)。

如果你正在进行多重继承,你必须深入挖掘,并且可能会更仔细地重读相同的文章以了解这些警告。并且它也只是在多重继承期间,当你可能会对父母的显式调用可能比super更好时,但没有特定的情况,没有人可以告诉你是否应该使用super

Python 3.x中super的唯一变化是你不需要显式地将当前类和super传递给它。这使得self更具吸引力,因为使用它意味着不会对父类或当前类进行硬编码。


1
投票

@Sven Marnach:

您的示例的问题在于您将Blogged中的显式超类调用superB.__init__与B中的Logger.__init__混合。这将无法正常工作。要么使用所有显式超类调用,要么在所有类上使用super()。当你使用super()时,你需要在所有涉及的类上使用它,包括A我认为。同样在你的例子中,我认为你可以在所有类中使用显式超类调用,即在B类中使用super()

当没有钻石继承时,我认为super()没有太多优势。然而,问题是你事先不知道你是否会在未来进入任何钻石继承,所以在这种情况下,无论如何都要明智地使用A.__init__(但是一直使用它)。否则,您最终将不得不在以后更改所有类或遇到问题。

© www.soinside.com 2019 - 2024. All rights reserved.