为什么在Python-2.x中打破了super()?

Mat*_*ner 41 python multiple-inheritance python-2.x super python-3.x

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

super(ThisClass, self).some_func(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

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

Sve*_*ach 42

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 Code Online (Sandbox Code Playgroud)

想象一下,这些类中有几个,都有单独的构造函数原型,并且都具有相同的接口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
Run Code Online (Sandbox Code Playgroud)

事情停止了.该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)
Run Code Online (Sandbox Code Playgroud)

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

这个和类似的故事是我认为不super() 应该被认为是调用基类方法的标准方法的原因.这并不意味着super()被打破.

  • 目前还不清楚子类化如何比`self._logger = Logger("B")更好 - 在树中混合任意类从未起作用.你可以想到一个类似的破坏的例子,但直接调用.这些类需要设计为协同工作.如果不是,那么`super`可以提供帮助.但是,正如文章所述,在调用超类时,最好将参数作为关键字传递.如果`Logger`类正确使用`super`,那么你的问题就不存在了. (7认同)
  • 对于那些未来的人来说,Raymond Hettinger给出了一个很棒的话题:Super被认为是Super.](https://www.youtube.com/watch?v=EiOglTERPEo) (3认同)

Len*_*bro 31

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

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

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

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

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

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

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

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

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

呃.好?

  • super() 不会打电话给你的超类.

是的,你这么说.

  • 不要混合super()和指挥呼叫.

是的,你也说过.

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

这并不意味着它被"破坏"或者应该"避免".

  • 马特,试图侮辱回复你的人,对你的技术论点毫无帮助. (9认同)
  • 我不知道你的意思. (3认同)

Dev*_*rre 6

你似乎暗示你的帖子

def some_func(self, *args, **kwargs):
    self.__class__.some_func(self, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

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

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

但是,在3.x中,需要的参数较少:您可以super().foo(*args, **kwargs)代替super(ThisClass, self).foo(*args, **kwargs).


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

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

  • @Sven`super`是创建类似于通用解决方案(跨越不同类型的继承图)的唯一方法,据我所知.当然,如果你对一般解决方案不感兴趣,那么`super`不会给你任何东西.另一方面,你也不会因为使用`super`而失去任何东西.所以,我会使用`super`. (3认同)
  • 我非常不同意`super()`是涉及多重继承时的"唯一希望".有些clas hierachies需要`super()`,但许多具有多重继承的类层次结构都没有. (2认同)
  • @Sven:但这就是它的全部*点.如果你不确切知道需要调用哪种方法,可以使用`super()`.:-)如果你这样做,你可以打电话给它. (2认同)