Подтвердить что ты не робот

Не активируются ли функции Mixin класса __init__?

Я хотел бы использовать Mixin, чтобы всегда добавлять некоторые функции init к моим дочерним классам, каждый из которых наследуется от разных базовых классов API. В частности, я хотел бы создать несколько разных дочерних классов, которые наследуют один из этих базовых классов, поставляемых API, и один Mixin, который всегда будет иметь код инициализации Mixin, выполненный таким же образом, без репликации кода. Тем не менее, кажется, что функция __init__ класса Mixin никогда не вызывается, если я явно не назову ее в функции класса __init__ класса Child, которая является менее идеальной. Я создал простой тестовый пример:

class APIBaseClassOne(object):
    def __init__(self, *args, **kwargs):
        print (" base ")

class SomeMixin(object):
    def __init__(self, *args, **kwargs):
        print (" mixin before ")
        super(SomeMixin, self).__init__(*args, **kwargs)
        print (" mixin after ")

class MyClass(APIBaseClassOne):
    pass

class MixedClass(MyClass, SomeMixin):
    pass

Как вы можете видеть в следующем выпуске, инициализация функции Mixin никогда не вызывается:

>>> import test
>>> test.MixedClass()
 base
<test.MixedClass object at 0x1004cc850>

Есть ли способ сделать это (иметь функцию init в вызове Mixin), не записывая каждый дочерний класс для явного вызова функции инициализации Mixin? (т.е. без необходимости делать что-то подобное в каждом классе:)

class MixedClass(MyClass, SomeMixin):
    def __init__(*args, **kwargs):
        SomeMixin.__init__(self, *args, **kwargs)
        MyClass.__init__(self, *args, **kwargs) 

Btw, если все мои дочерние классы наследовались от одного базового класса, я понимаю, что могу создать новый средний класс, который наследуется от базового класса и mixin и держит его DRY таким образом. Однако они наследуются от разных базовых классов с общей функциональностью. (Классы Django Field, если быть точным).

4b9b3361

Ответ 1

Извините, я видел это так поздно, но

class MixedClass2(SomeMixin, MyClass):
    pass

>>> m = MixedClass2()
 mixin before 
 base 
 mixin after

Образец @Ignacio говорит о том, что он называется совместным множественным наследованием, и это здорово. Но если базовый класс не заинтересован в сотрудничестве, сделайте его второй базой, а ваш mixin первым. Mixin __init__() (и все, что он определяет) будет проверяться перед базовым классом, следуя Python MRO.

Это должно решить общий вопрос, хотя я не уверен, что он справится с вашим конкретным использованием. Базовые классы с пользовательскими метаклассами (например, модели Django) или со странными декораторами (например, @martineau answer;) могут делать сумасшедшие вещи.

Ответ 2

Попросите базовый класс вызвать super().__init__(), хотя он является подклассом object. Таким образом будут запущены все методы __init__().

class BaseClassOne(object):
    def __init__(self, *args, **kwargs):
        super(BaseClassOne, self).__init__(*args, **kwargs)
        print (" base ")

Ответ 3

Python не выполняет никаких неявных вызовов методам __init__ класса super-class (es), но это позволяет сделать это автоматически. Один из способов заключается в определении метакласса для вашего смешанного класса (классов), который создает или расширяет метод смешанного класса __init__, чтобы он вызывал все перечисленные базы "__init__" в том порядке, в котором они были перечислены.

Второй способ - использовать декоратор класса, который показан ниже в разделе под названием Изменить.

Использование метакласса:

class APIBaseClassOne(object):  # API class (can't be changed)
    def __init__(self, *args, **kwargs):
        print '  APIBaseClassOne.__init__()'

class SomeMixin(object):
    def __init__(self, *args, **kwargs):
        print '  SomeMixin.__init__()'

class MixedClassMeta(type):
    def __new__(cls, name, bases, classdict):
        classinit = classdict.get('__init__')  # Possibly None.

        # define an __init__ function for the new class
        def __init__(self, *args, **kwargs):
            # call the __init__ functions of all the bases
            for base in type(self).__bases__:
                base.__init__(self, *args, **kwargs)
            # also call any __init__ function that was in the new class
            if classinit:
                classinit(self, *args, **kwargs)

        # add the local function to the new class
        classdict['__init__'] = __init__
        return type.__new__(cls, name, bases, classdict)

class MixedClass(APIBaseClassOne, SomeMixin):
    __metaclass__ = MixedClassMeta  # important
    # if exists, is called after the __init__ of all the direct bases
    def __init__(self, *args, **kwargs):
        print '  MixedClass.__init__()'

print('MixedClass():')
MixedClass()

Вывод:

MixedClass():
  APIBaseClassOne.__init__()
  SomeMixin.__init__()
  MixedClass.__init__()

Edit

Здесь, как выполнить одно и то же с декоратором класса (требуется Python 2.6 +):

class APIBaseClassOne(object):  # API class (can't be changed)
    def __init__(self, *args, **kwargs):
        print)'  APIBaseClassOne.__init__()')

class SomeMixin(object):
    def __init__(self, *args, **kwargs):
        print('  SomeMixin.__init__()')

def mixedomatic(cls):
    """ Mixed-in class decorator. """
    classinit = cls.__dict__.get('__init__')  # Possibly None.

    # define an __init__ function for the class
    def __init__(self, *args, **kwargs):
        # call the __init__ functions of all the bases
        for base in cls.__bases__:
            base.__init__(self, *args, **kwargs)
        # also call any __init__ function that was in the class
        if classinit:
            classinit(self, *args, **kwargs)

    # make the local function the class __init__
    setattr(cls, '__init__', __init__)
    return cls

@mixedomatic
class MixedClass(APIBaseClassOne, SomeMixin):
    # if exists, is called after the __init__ of all the direct base classes
    def __init__(self, *args, **kwargs):
        print('  MixedClass.__init__()')

print('MixedClass():')
MixedClass()

(для Python < 2.6 используйте MixedClass = mixedomatic(MixedClass) после определения класса.)