Python的super()、抽象基类和NotImplementedError
抽象基类在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