Python: наследуйте суперкласс __init__

У меня есть базовый класс с большим количеством аргументов __init__:

def BaseClass(object):
    def __init__(self, a, b, c, d, e, f, ...):
        self._a=a+b
        self._b=b if b else a
        ...

Все наследующие классы должны запускать метод __init__ базового класса.

Я могу написать метод __init__() в каждом из наследующих классов, который вызовет суперкласс __init__, но это будет серьезное дублирование кода:

def A(BaseClass):
    def __init__(self, a, b, c, d, e, f, ...):
        super(A, self).__init__(a, b, c, d, e, f, ...)

def B(BaseClass):
    def __init__(self, a, b, c, d, e, f, ...):
        super(A, self).__init__(a, b, c, d, e, f, ...)

def C(BaseClass):
    def __init__(self, a, b, c, d, e, f, ...):
        super(A, self).__init__(a, b, c, d, e, f, ...)

...

Какой самый питоновский способ автоматического вызова суперкласса __init__?

Ответ 1

super(SubClass, self).__init__(...)

Рассмотрите возможность использования * args и ** kw, если это поможет решить ваш ночной кошмар.

Ответ 2

Вы должны написать это явно, но, с другой стороны, если у вас много аргументов, вы, вероятно, должны использовать * args для позиционных аргументов и ** kwargs для ключевых слов args.

class SubClass(BaseClass):
    def __init__(self, *args, **kwargs):
        super(SubClass, self).__init__(*args, **kwargs)
        # SubClass initialization code

Другим способом, который вы можете использовать, является минимизация кода в init, а затем в конце функции init вызвать другую пользовательскую функцию. Затем в подклассе вам просто нужно переопределить пользовательскую функцию

class BaseClass(object):
    def __init__(self, *args, **kwargs):
        # initialization code
        self._a = kwargs.get('a')
        ...
        # custom code for subclass to override
        self.load()

    def load():
        pass

class SubClass(BaseClass)
    def load():
        # SubClass initialization code
        ...

Ответ 3

Если производные классы не реализуют ничего, кроме того, что уже делает базовый класс __init__(), просто опустите методы производных классов __init__() - тогда автоматически вызывается базовый класс __init__().

Если, OTOH, ваши производные классы добавляют дополнительную работу в свой __init__(), и вы не хотите, чтобы они явно вызывали свой базовый класс __init__(), вы можете сделать это:

class BaseClass(object):
    def __new__(cls, a, b, c, d, e, f, ...):
        new = object.__new__(cls)
        new._a=a+b
        new._b=b if b else a
        ...
        return new

class A(BaseClass):
    ''' no __init__() at all here '''

class B(BaseClass):
    def __init__(self, a, b, c, d, e, f, ...):
        ''' do stuff with init params specific to B objects '''

Так как __new__() всегда вызывается автоматически, дальнейшая работа в производных классах не требуется.

Ответ 4

Если вы не используете что-то полезное в методах подкласса __init__(), вам не нужно переопределять его.

def BaseClass(object):
    def __init__(self, a, b, c, d, e, f, ...):
        self._a=a+b
        self._b=b if b else a
        ...

def A(BaseClass):
    def some_other_method(self):
        pass

def B(BaseClass):
    pass

Ответ 5

В версии 2.6 и более поздней версии для наследования init из базового класса нет суперфункции, вы можете наследовать ниже:

class NewClass():
     def __init__():
         BaseClass.__init__(self, *args)

Ответ 6

Добавление реализации Pythonic. Предполагая, что вы хотите передать все атрибуты, вы можете использовать приведенный ниже код. (Можно также сохранить/удалить определенные ключи kwargs, если вы хотите подмножество).

def A(BaseClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

base = BaseClass(...)
new = A( **base.__dict__ )