2014-03-25 14 views
6

Versuchen herauszufinden, wie Sie einige Mixins für Django Management-Befehl schreiben, die BaseCommand.option_list Wrap, ohne den Wert der aktuellen Klasse oder alle geerbten Klassen/Mixins zu verlieren. Das Ziel ist es, zu vermeiden, BaseCommand.option_list + MyCommonOptionMixin.option_list + MyOtherCommonOptionMixin.option_list + (local command options) in meinen Befehlen zu tun.Python Mixin zum Erweitern Klasseneigenschaft

Beispiel:

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

    # Rest of BaseCommand code. 

Ich definiere einen mixin mit einigen gemeinsamen Optionen:

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 

Vielleicht habe ich eine mehr, nur diesen Fall abzudecken, in denen ich mehrere haben. Die Mixins beide Überschreibung __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. 
    ) 

Ich habe in Kollision ausgeführt werden, wenn die Mixins den gleichen Namen für die option_list Eigenschaft verwenden. Gibt es einen saubereren Weg, dieses Ziel zu erreichen, als die option_list in den Mixins eindeutig zu benennen und __getattribute__ zu überschreiben?

Antwort

5

Der Rat in der documentation ist, die verschiedenen Optionslisten explizit zu verketten. Das heißt, wenn Sie diesen Weg gehen wollen, denke ich, eine benutzerdefinierte Metaklasse ist der richtige Ansatz. Etwas wie:

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