Python 管理多个mixin-使用元类更好?
我有一组可以动态添加到基类并进行混合和匹配的混合。它们都具有相似的属性和方法,但在组合多个mixin时不应相互重写。构建一个常规的类MixinManager来处理事情,还是构建一个元类来更改我的主基类的行为(基于哪些mixin组合在一起)更好 例如,我的mixins all重新定义了Python 管理多个mixin-使用元类更好?,python,function,class,mixins,metaclass,Python,Function,Class,Mixins,Metaclass,我有一组可以动态添加到基类并进行混合和匹配的混合。它们都具有相似的属性和方法,但在组合多个mixin时不应相互重写。构建一个常规的类MixinManager来处理事情,还是构建一个元类来更改我的主基类的行为(基于哪些mixin组合在一起)更好 例如,我的mixins all重新定义了\u full方法。但是,我需要一个full方法,该方法调用混合到基类中的\u full方法 混合类 这些mixin共享通用的方法和属性。我希望避免覆盖每个mixin子类中的所有基本方法。这是很多重复的代码 impo
\u full
方法。但是,我需要一个full
方法,该方法调用混合到基类中的\u full
方法
混合类
这些mixin共享通用的方法和属性。我希望避免覆盖每个mixin子类中的所有基本方法。这是很多重复的代码
import six
import abc
import inspect
import sys
class BaseMixin(six.with_metaclass(abc.ABCMeta, object)):
@abc.abstractmethod
def _full(self):
pass
# not working yet
def has_name(self):
return self._name is not None
class AMixin(BaseMixin):
def __init__(self, db_name=None, **kwargs):
super(AMixin, self).__init__(**kwargs)
self.db_name = db_name
def _full(self):
print('i am an A mixin', self.db_name)
class BMixin(BaseMixin):
def __init__(self, ext_name=None, **kwargs):
super(BMixin, self).__init__(**kwargs)
self.ext_name = ext_name
def _full(self):
print('i am a B mixin', self.ext_name)
MIXINS = {name.lower().split('mixin')[0]: obj for name, obj in inspect.getmembers(sys.modules[__name__], inspect.isclass)
if 'Mixin' in name and 'Base' not in name}
混合管理器类
我可以通过一个常规的班级管理系统来实现。但是,我需要设置许多“管理器”方法和属性,这些方法和属性看起来会变得很难处理。我不想硬编码定义我需要为其执行此操作的每个方法/属性
class MixinManager(object):
def __new__(cls, *args, **kwargs):
mixins = [obj for obj in cls.__bases__ if 'Mixin' in obj.__name__]
if len(mixins) == 1:
setattr(cls, 'full', mixins[0]._full)
return super(MixinManager, cls).__new__(cls, args, kwargs)
def full(self, mixin=None):
if mixin:
assert mixin.lower() in MIXINS.keys(), 'mixin must be one of {0}'.format(MIXINS.keys())
mix_obj = MIXINS[mixin]
mix_obj._full(self)
元类
当我只有一个mixin(例如TestA、TestB)时,这是可行的,但当我有多个mixin(例如TestAB)时,这会失败。我无法使用full
方法实现此功能。有没有办法让元类在我的类上定义一个新的full
方法来控制如何在每个实例上调用它?我不知道该怎么做。我在这里使用\uuuu call\uuuu
,因为我想控制在第一次调用每个实例时如何构造类
class MixMeta(abc.ABCMeta):
def __new__(cls, *args, **kwargs):
return super(MixMeta, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('---')
print('call mixmeta', cls, args, kwargs)
mixins = [obj for obj in cls.__bases__ if 'Mixin' in obj.__name__]
newclass = super(MixMeta, cls).__call__(*args, **kwargs)
if len(mixins) == 1:
setattr(newclass, 'full', mixins[0](*args, **kwargs)._full)
else:
setattr(newclass, 'full', cls.full)
return newclass
def full(self, mixin=None):
print('in full', self)
if mixin:
assert mixin.lower() in MIXINS.keys(), 'mixin must be one of {0}'.format(MIXINS.keys())
mix_obj = MIXINS[mixin]
print('mixobj', mix_obj)
mix_obj(*self._args, **self._kwargs)._full()
基本对象
实例化每个类
base = TestBase()
a = TestA(db_name='dba') (would like name='dba'; maps to db_name)
b = TestB(ext_name='extb') (would like name='extb' ...)
ab = TestAB(db_name='dba') (name ambiguous; this maps to db_name)
ab1 = TestAB(db_name='dba', ext_name='extb') (same as above, both names map)
ab2 = TestAB(db_name='dba', ext_name='extb')
使用MixinManager,这可以正常工作。对于MixMeta,它无法正确调用
a.full() # should print from AMixin
b.full() # should print from BMixin
ab.full() # should not print anything or throw ambiguity error
ab1.full(mixin='a') # should print from AMixin
ab2.full(mixin='b') # should print from BMixin
同样,我想对属性做同样的事情。我想将一个通用的名称
属性映射到\u名称
,并动态映射到正在调用的mixin。但是,当多个mixin组合在一起时,名称不应覆盖
a.has_name() # should find and print db_name from AMixin (only db_name set)
b.has_name() # should find and print ext_name from BMixin (only ext_name set)
ab.has_name() # should print nothing or throw ambiguity error
ab1.has_name(mixin='a') # should print db_name (both db and ext names set)
ab2.has_name(mixin='b') # should print ext_name (both db and ext names set)
您试图解决的实际问题是什么?我希望有一个方法或属性,根据所使用的mixin调用底层mixin方法或属性。我不知道会提前使用哪些混合器。这不是实际问题,这是您解决方案的一部分。好吧,我的问题更像是一个问题。使用元类动态管理我的mixin还是使用常规类更好?一个问题是,这一行在我的元类尝试中失败了,
mix\u obj(*self.\u args,**self.\u kwargs)。\u full()
。我不知道该怎么称呼这件事。那将是一个基于意见的问题。但是一般来说,您应该考虑类的任何类型的动态添加,这些类增加了复杂性,降低了可读性,防止了IDE中的自动完成,并使调试变得更加复杂。所以,明智地使用它!
a.has_name() # should find and print db_name from AMixin (only db_name set)
b.has_name() # should find and print ext_name from BMixin (only ext_name set)
ab.has_name() # should print nothing or throw ambiguity error
ab1.has_name(mixin='a') # should print db_name (both db and ext names set)
ab2.has_name(mixin='b') # should print ext_name (both db and ext names set)