Питон подмешать, чтобы продлить свойство класса


Пытаюсь понять, как написать некоторые миксины для команды управления Django, которые будут обертывать BaseCommand.option_list без потери значения текущего класса или любых унаследованных классов / миксинов. Цель состоит в том, чтобы избежать выполнения BaseCommand.option_list + MyCommonOptionMixin.option_list + MyOtherCommonOptionMixin.option_list + ( local command options ) в моих командах.

Пример:

class BaseCommmand(object):
    option_list = (
        # Default options here.
    )

    # Rest of BaseCommand code. 

Я определяю миксин с некоторыми общими вариантами:

class MyCommonOptionMixin(object):
    option_list = (
        # Some common option/options I wish to have available in many commands
    )

    def __getattribute__(self, name):
        values = super(MyCommonOptionMixin, self).__getattribute__(name)
        if name == 'option_list':
            for option in self.option_list:
                if option not in values:
                    values += option,
        return values

Может быть, у меня есть еще один, просто чтобы покрыть тот случай, когда у меня есть несколько. Оба миксина переопределяют __getattribute__

class MyOtherCommonOptionMixin(object):
    option_list = (
        # Maybe I have another mixin I want to define separately
    )

    # Tried this, does not work with more than one mixin. 
    def __getattribute__(self, name):
        values = super(MyOtherCommonOptionMixin, self).__getattribute__(name)
        if name == 'option_list':
            for option in self.option_list:
                if option not in values:
                    values += option,
        return values

    # Works if the mixin defines the option_list under a different name, e.g. "_mymixin_options"
    # Then access at self._mymixin_options instead of self.option_list


class MyCommand(MyCommonOptionMixin, MyOtherCommonOptionMixin, BaseCommand):
    option_list = BaseCommand.option_list + (
        # Local defined options.
    )

Я столкнулся с столкновением, если миксины используйте то же имя для свойства option_list. Есть ли более чистый способ достичь этой цели, чем называть option_list однозначно внутри микшинов и переопределять __getattribute__?

1 6

1 ответ:

Совет в документации состоит в том, чтобы явно объединить различные списки опций. Тем не менее, если вы хотите пойти этим путем, я думаю, что пользовательский метакласс-это правильный подход. Что-то вроде:

class CommandMetaclass(type):
    def __new__(mcl, name, bases, dct):
        # start with the option_list in the class we're creating
        # use set() to avoid duplicates
        option_list = set(dct.get('option_list', tuple()))

        # add the options from each base class
        for base in bases:
            option_list.update(base.__dict__.get('option_list', tuple()))

        # replace the original option_list with our combined version
        dct['option_list'] = tuple(option_list)

        return type.__new__(mcl, name, bases, dct)

class MyCommand(MyCommonOptionMixin, MyOtherCommonOptionMixin, BaseCommand):
    __metaclass__ = CommandMetaclass

    option_list = ( 
        # Local defined options.
    )