Python 管理多个mixin-使用元类更好?

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

我有一组可以动态添加到基类并进行混合和匹配的混合。它们都具有相似的属性和方法,但在组合多个mixin时不应相互重写。构建一个常规的类MixinManager来处理事情,还是构建一个元类来更改我的主基类的行为(基于哪些mixin组合在一起)更好

例如,我的mixins all重新定义了
\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)