Python-2.x中的super()是否存在问题?
很多人说在Python 2中应该避免使用super
。我在使用Python 2的super
时发现,它的表现总是和我预期的不一样,除非我提供所有的参数,比如下面这个例子:
super(ThisClass, self).some_func(*args, **kwargs)
在我看来,这样做就失去了使用super()
的意义,因为它既不简洁,也没有比直接调用TheBaseClass.some_func(self, *args, **kwargs)
好多少。对于大多数情况来说,方法解析顺序就像一个遥不可及的童话。
- 除了
2.7是Python 2的最后一个主要版本
这一点,为什么在Python 2中super
依然有问题呢? - Python 3中的super是如何变化的?有什么需要注意的地方吗?
- 未来我什么时候以及为什么应该使用
super
呢?
5 个回答
你在帖子里似乎暗示
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。它的行为只有在涉及多重继承时才会显得“奇怪”,而在多重继承的情况下,super()
基本上是你获得正确解决方案的唯一希望。在单继承中,它只是比SuperClass.foo(self, *args, **kwargs)
稍微多一些文字,但没有其他不同。
我觉得我同意Sven的看法,认为这种多重继承是值得避免的,但我不同意说super
值得避免。如果你的类是为了被继承而设计的,super
给使用你类的用户提供了希望,让他们在需要多重继承时能够正常工作,这样会让你的类更好用。
super()
在 Python 2 和 Python 3 中并没有问题。
我们来看看这篇博客中的观点:
- 它的功能和听起来的不一样。
好吧,这个观点你可能同意也可能不同意,毕竟这是主观的。那么它应该叫什么呢?super()
是用来替代直接调用父类的,所以这个名字我觉得还不错。它并不是直接调用父类,因为如果只是这样做,那就没什么意义了,因为你本来就可以直接调用。确实,这一点可能不太明显,但需要用到 super()
的情况通常也不容易发现。如果你需要它,那你可能正在处理一些复杂的多重继承。这种情况不会很明显。(或者你在做简单的混合类,这种情况下就会很明显,并且即使你没看文档,它的表现也会如你所期待的那样。)
如果你可以直接调用父类,那你可能最终会选择这样做。这是最简单、最直观的方式。super()
只有在直接调用不奏效时才会派上用场。
- 它和直接调用父类不太兼容。
是的,因为它是为了解决直接调用父类时遇到的问题而设计的。你只有在确切知道父类是什么的情况下,才能直接调用父类。比如在使用混合类时,你并不知道父类是什么,或者当你的类层次结构非常混乱,以至于你实际上是在合并两个分支时(这也是使用 super()
的典型例子)。
所以,只要你的类层次结构中的每个类都有明确的位置,直接调用父类是可行的。如果没有,那就不行,这时候你必须使用 super()
。super()
的作用就是根据方法解析顺序(MRO)来找出“下一个父类”,而不需要你明确指定,因为你并不总是知道它是什么,尤其是在使用混合类时。
- 完全不同的编程语言 Dylan,类似于一种 Lisp 的东西,以另一种方式解决了这个问题,但这种方式在 Python 中无法使用,因为它们非常不同。
嗯,好吧?
super()
并不直接调用你的父类。
是的,你已经说过了。
- 不要混用
super()
和直接调用。
是的,你也说过了。
所以,有两个反对的观点:1. 名字不好。2. 你必须一致使用。
这并不意味着它是“坏的”或者应该“避免使用”。
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()
是坏的。