在Python 2中经常声明super
应该是avoided。我在Python 2中使用super
时发现,除非我提供,否则它永远不会按照我的预期行事所有参数如示例:
super(ThisClass, self).some_func(*args, **kwargs)
在我看来,这违背了使用super()
的目的,它既不简洁,也不比TheBaseClass.some_func(self, *args, **kwargs)
好。在大多数情况下,方法解析顺序是一个遥远的童话故事。
super
?答案 0 :(得分:40)
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
无法对其执行任何操作。除了删除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()
被打破了。
答案 1 :(得分:30)
super()
没有被破坏。
让我们考虑博客文章中的论点:
好的,你可能同意或不同意,这是非常主观的。应该怎么称呼呢? super()
是直接调用超类的替代品,所以这个名字对我来说似乎很好。它不会直接调用超类,因为如果这就是它所做的全部,那将毫无意义,因为无论如何你都可以这样做。不可否认,这可能并不明显,但您需要super()
的情况通常并不明显。如果你需要它,你正在做一些非常多毛的多重继承。这不会很明显。 (或者你正在做一个简单的混音,在这种情况下,即使你没有阅读文档,它也会非常明显并且表现得如你所愿。)
如果你可以直接调用超类,这可能就是你最终要做的事情。这是一种简单直观的方法。 super()
只有在不起作用时才会发挥作用。
是的,因为它旨在解决这样做的问题。您可以直接调用超类,只有当您确切知道该类是什么时,才能直接调用它。例如,你没有使用mixins,或者当你的类层次结构混乱,你实际上正在合并两个分支时(这是使用super()
的所有示例中的典型示例)。
因此,只要类层次结构中的每个类都有一个定义良好的位置,就可以直接调用超类。如果不这样做,则它不起作用,在这种情况下,您必须使用super()
。这就是super()
根据MRO确定“下一个超类”的含义,而不必明确指定它,因为你不能总是这样做因为你并不总是知道它是什么例如,当使用mixins时。
的Eh。 OK?
super()
不会调用您的超类。super()
并直接致电。所以,有两个反对它的论据:1。名字不好。 2.你必须始终如一地使用它。
这并不意味着它被“破坏”或者应该“避免”。
答案 2 :(得分: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 :(得分:3)
您是否阅读了链接它的文章?它没有得出结论:super
应该避免,但是在使用它时你应该警惕它的警告。文章总结了这些警告,但我不同意他们的建议。
本文的要点是多重继承可能会变得混乱,而super
并没有像作者想要的那样多。但是,在没有super
的情况下进行多重继承通常会更复杂。
如果您没有进行多重继承,super
为您提供的优势是,从您的类继承的任何人都可以添加简单的mixins,并且可以正确调用它们的__init__
。请记住,即使您从__init__
继承,也要始终调用超类的object
,并将所有剩余的参数(*a
和**kw
)传递给它。当你从父类调用其他方法时也使用super
,但这次使用你已经知道的正确签名(即确保它们在所有类中都有相同的签名)。
如果你正在进行多重继承,你必须深入挖掘,并且可能会更仔细地重读相同的文章以了解这些警告。并且它也仅在多重继承期间,当您对父项的显式调用可能优于super
时,但没有特定情况,没有人可以告诉您是否应该使用super
。
Python 3.x中super
的唯一变化是您不需要将当前类和self
显式传递给它。这使得super
更具吸引力,因为使用它意味着不会对父类或当前类进行硬编码。
答案 4 :(得分:1)
@Sven Marnach:
您的示例的问题在于您将Blogged中的显式超类调用B.__init__
和Logger.__init__
与B中的super()
混合在一起。这不起作用。要么使用所有显式超类调用,要么对所有类使用super()
。当您使用super()
时,您需要在所有涉及的课程中使用它,包括A我认为。同样在您的示例中,我认为您可以在所有类中使用显式超类调用,即在B类中使用A.__init__
。
当没有钻石继承时,我认为super()没有太大的优势。然而,问题是你事先不知道你将来是否会进入任何钻石继承,所以在这种情况下,无论如何都要使用super()
是明智的(但是一直使用它)。否则,您最终将不得不在以后更改所有课程或遇到问题。