Python's super(), abstract base classes, and NotImplementedError

前端 未结 3 1613
慢半拍i
慢半拍i 2021-02-03 14:45

Abstract base classes can still be handy in Python. In writing an abstract base class where I want every subclass to have, say, a spam() method, I want to write som

相关标签:
3条回答
  • 2021-02-03 15:07

    The key point to understand this is super() is for implementing cooperative inheritance. How the classes cooperate is up to you the programmer. super() is not magic and does not know exactly what you want! There is not much point in using super for a flat hierarchy that doesn't need cooperative inheritance, so in that case S. Lott's suggestion is spot on. Subclasses of Useful may or may not want to use super() depending their goals :)

    For example: Abstract is A. A <- B, but then you want to support insertion of C like so A <- 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()
    

    And the output:

    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
    
    0 讨论(0)
  • 2021-02-03 15:15

    You can do this cleanly in python 2.6+ with the 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()
    

    The output will be

    In B
    In C
    
    0 讨论(0)
  • 2021-02-03 15:18

    Do not write all that code. Simple inspection of the abstract class can save you writing all that code.

    If the method is abstract, the concrete subclass does not call super.

    If the method is concrete, the concrete subclass does call super.

    0 讨论(0)
提交回复
热议问题