【问题标题】:Python's super(), abstract base classes, and NotImplementedErrorPython 的 super()、抽象基类和 NotImplementedError
【发布时间】:2011-06-15 12:35:53
【问题描述】:

Abstract base classes can still be handy in 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.")

对于一个简单的子类来说还可以,但是当编写一个有很多方法的类时,try-except 的事情变得有点麻烦,而且有点难看。有没有更优雅的从抽象基类子类化的方式?我只是做错了吗?

【问题讨论】:

  • 这没什么意义。您应该知道实现了哪些超类方法(对于哪些super 有意义),哪些没有实现,因为它们是抽象的。您可以阅读源代码。
  • raise SyntaxError 也是该语言。问题是“当简单检查抽象类就可以省去编写所有代码时,为什么还要编写所有代码”?
  • @S.Lott 啊,现在明白了。顺便说一句,您应该将其作为答案提交,因为它是。

标签: python abstract-class super


【解决方案1】:

您可以在 python 2.6+ 中使用 abc module 干净地做到这一点:

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

【讨论】:

  • 这不一定相同 - 不能从您的示例中实例化 B。
  • 有趣!我什至没有注意到 abc 模块的添加!
  • @Tomasz 也很有趣。那么,如果一个人不能实例化这个类,那么对单元测试有什么影响呢?
  • 如果您将B.foo 的正文更改为raise NotImplementedError,它不会绕过这个问题。仍然值得通知程序员,“嘿,笨蛋,你忘记覆盖这个抽象方法了!”目前我不确定abc 是如何真正解决这个问题的。
  • 啊!我做错了;您在B 中定义def foo(): pass,然后您不能实例化任何子类,直到您在某些子类中覆盖B.foo。基本上,除非您覆盖所有未定义的方法,否则您将无法实例化该子类。非常整洁!
【解决方案2】:

不要编写所有代码。对抽象类的简单检查可以节省您编写所有代码的时间。

如果方法是抽象的,具体的子类不会调用super。

如果方法是具体的,具体的子类会调用 super。

【讨论】:

  • 这是一个简单的解决方案,除非您要进行不同类型的协作继承。 IE。摘要是A.A
【解决方案3】:

理解这一点的关键是super()是为了实现协作继承。类如何合作取决于程序员。 super() 不是魔法,也不知道你到底想要什么!将 super 用于不需要协作继承的平面层次结构没有多大意义,因此在这种情况下,S. Lott 的建议是正确的。 Useful 的子类可能想也可能不想使用super(),这取决于他们的目标:)

例如:Abstract 是 A.A

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-06
    • 2016-07-05
    • 1970-01-01
    • 2018-07-11
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多