Python的super()、抽象基类和NotImplementedError

10 投票
3 回答
21652 浏览
提问于 2025-04-16 10:37

抽象基类在Python中仍然很有用。 我在写一个抽象基类的时候,希望每个子类都有一个叫做spam()的方法,我想写成这样:

class Abstract(object):
    def spam(self):
        raise NotImplementedError

问题在于,我还想使用super(),并且要正确地把它放在所有子类的调用链中。在这种情况下,我似乎必须像下面这样包裹每一个super调用:

class Useful(Abstract):
    def spam(self):
        try:
            super(Useful, self).spam()
        except NotImplementedError, e:
            pass
        print("It's okay.")

对于一个简单的子类来说,这样做还可以,但如果我写的类有很多方法,这种尝试-异常的处理方式就显得有点繁琐,也不太美观。有没有更优雅的方法来从抽象基类继承呢?我是不是做错了?

3 个回答

5

理解这一点的关键是,super() 是用来实现合作继承的。也就是说,类之间如何合作,完全取决于你这个程序员。super() 不是魔法,它并不知道你具体想要什么!如果你的类结构很简单,不需要合作继承,那么使用 super() 就没什么意义。在这种情况下,S. Lott 的建议是非常正确的。对于 Useful 的子类来说,是否使用 super() 取决于它们的目标 :)

举个例子:假设有一个抽象类 A,然后有一个类 B 继承自 A,但你想要插入一个类 C,变成 A 继承 C,C 再继承 B。

class A(object):                                                                                         
    """I am an abstract abstraction :)"""
    def foo(self):
        raise NotImplementedError('I need to be implemented!')

class B(A):
    """I want to implement A"""
    def foo(self):
        print('B: foo')
        # MRO Stops here, unless super is not A
        position = self.__class__.__mro__.index
        if not position(B) + 1 == position(A):
            super().foo()

b = B()     
b.foo()

class C(A):
    """I want to modify B and all its siblings (see below)"""
    def foo(self):
        print('C: foo')
        # MRO Stops here, unless super is not A
        position = self.__class__.__mro__.index
        if not position(C) + 1 == position(A):
            super().foo()

print('')   
print('B: Old __base__ and __mro__:\n')
print('Base:', B.__bases__)
print('MRO:', B.__mro__)
print('')
# __mro__ change implementation
B.__bases__ = (C,)
print('B: New __base__ and __mro__:\n')
print('Base:', B.__bases__)
print('MRO:', B.__mro__)
print('')
b.foo()

然后输出结果是:

B: foo

B: Old __base__ and __mro__:

Base: (<class '__main__.A'>,)
MRO: (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

B: New __base__ and __mro__:

Base: (<class '__main__.C'>,)
MRO: (<class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

B: foo
C: foo
8

别写那么多代码。简单看看抽象类就能帮你省下很多代码。

如果这个方法是抽象的,具体的子类就不需要调用父类的方法。

如果这个方法是具体的,具体的子类就需要调用父类的方法。

11

你可以在 Python 2.6 及以上版本中,使用 abc 模块 来干这件事,方法很简单:

import abc
class B(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def foo(self):
        print 'In B'

class C(B):
    def foo(self):
        super(C, self).foo()
        print 'In C'

C().foo()

运行后会得到这样的结果:

In B
In C

撰写回答