Python 混入以扩展类属性
我正在尝试为Django的管理命令写一些混合类(mixins),目的是在不丢失当前类或任何继承类/混合类的值的情况下,包装BaseCommand.option_list
。这样做的目的是为了避免在我的命令中写成BaseCommand.option_list + MyCommonOptionMixin.option_list + MyOtherCommonOptionMixin.option_list + (本地命令选项)
。
举个例子:
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 个回答
5
在文档中提到的建议是,应该明确地把不同的选项列表连接起来。也就是说,如果你想这样做,我觉得使用一个自定义的元类是个不错的办法。可以像下面这样:
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.
)