Super (), абстрактные базовые классы Python и NotImplementedError - PullRequest
5 голосов
/ 26 января 2011

Абстрактные базовые классы все еще могут быть полезны в 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-Кроме становится немного громоздкой и немного уродливой. Есть ли более элегантный способ создания подклассов из абстрактных базовых классов? Я просто делаю это неправильно?

Ответы [ 3 ]

8 голосов
/ 26 января 2011

Вы можете сделать это чисто в 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
7 голосов
/ 26 января 2011

Не пишите весь этот код. Простая проверка абстрактного класса может сэкономить вам написание всего этого кода.

Если метод абстрактный, конкретный подкласс не вызывает super.

Если метод конкретный, конкретный подкласс вызывает супер.

3 голосов
/ 28 ноября 2012

Ключевым моментом для понимания этого является super() для реализации кооперативного наследования. Как классы взаимодействуют, решать программисту. super() не волшебство и не знает точно, что вы хотите! Нет смысла использовать super для плоской иерархии, которая не нуждается в кооперативном наследовании, поэтому в этом случае предложение S. Lott является точным. Подклассы Useful могут или не могут использовать super() в зависимости от своих целей:)

Например: Abstract - A. A <- B, но вы хотите поддержать вставку C, например, A <- C <- B. </p>

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
...