Python中的super方法及其调用替代方案

59 投票
2 回答
34286 浏览
提问于 2025-04-16 12:01

我到处看到的例子都说,父类的方法应该用下面这种方式来调用:

super(SuperClass, instance).method(args)

那么,使用下面这种方式调用有没有什么缺点呢:

SuperClass.method(instance, args)

2 个回答

9

其实没有什么惩罚,不过你的例子有点误导。在第一个例子中,应该是

super(SubClass, instance).method(args)  # Sub, not SuperClass

这让我想引用一下Python文档中的内容:

使用super有两个典型的场景。在单继承的类层次结构中,super可以用来引用父类,而不需要明确地写出它们的名字,这样可以让代码更容易维护。这种用法和其他编程语言中super的用法很相似。

第二种用法是为了支持在动态执行环境中的合作多重继承。这种用法是Python特有的,在静态编译语言或只支持单继承的语言中是找不到的。这使得实现“菱形图”成为可能,即多个基类实现同一个方法。好的设计要求这个方法在每种情况下都有相同的调用签名(因为调用的顺序是在运行时决定的,这个顺序会根据类层次结构的变化而变化,并且这个顺序可能包括在运行前未知的兄弟类)。

简单来说,使用第一种方法时,你不需要在单类层次结构中硬编码父类,而在使用多重继承时,第二种方法其实是无法高效或有效地实现你想要的功能。

115

考虑以下情况:

class A(object):
    def __init__(self):
        print('Running A.__init__')
        super(A,self).__init__()
class B(A):
    def __init__(self):
        print('Running B.__init__')        
        # super(B,self).__init__()
        A.__init__(self) 

class C(A):
    def __init__(self):
        print('Running C.__init__')
        super(C,self).__init__()
class D(B,C):
    def __init__(self):
        print('Running D.__init__')
        super(D,self).__init__()

foo=D()

这些类形成了一个所谓的继承菱形:

    A
   / \
  B   C
   \ /
    D

运行这段代码会得到:

Running D.__init__
Running B.__init__
Running A.__init__

这不好,因为 C__init__ 被跳过了。原因是 B__init__ 直接调用了 A__init__

super 的目的是解决继承菱形的问题。如果你取消注释

# super(B,self).__init__()

并注释掉

A.__init__(self) 

代码会得到更理想的结果:

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__

现在所有的 __init__ 方法都会被调用。注意,当你定义 B.__init__ 时,你可能会 认为 super(B,self).__init__() 和调用 A.__init__(self) 是一样的,但你会错的。在上面的情况中,super(B,self).__init__() 实际上是调用了 C.__init__(self)

天哪,BC 一无所知,但 super(B,self) 却知道要调用 C__init__?原因是 self.__class__.mro() 包含了 C。换句话说,self(在上面的例子中是 foo)知道 C 的存在。

所以要小心——这两者并不是可以互换的。它们可能会产生截然不同的结果。

使用 super 有陷阱。 这需要在继承图中的所有类之间进行相当程度的协调。(例如,它们必须对 __init__ 有相同的调用签名,因为任何特定的 __init__ 并不知道 super 可能接下来会调用哪个其他的 __init__,或者 使用 **kwargs。)此外,你必须在所有地方一致地使用 super。如果有一次跳过它(就像上面的例子),你就会失去使用 super 的整个意义。

如果你完全控制你的类层次结构,或者避免继承菱形,那么就不需要使用 super

撰写回答