如何在实例化时有条件地将mixin添加到当前类?

moo*_*nts 4 python class mixins

我想要一个基于传递给构造函数的参数添加mixins的类.这就是我尝试过的:

class MixinOne(object):
    def print_name(self):
        print("{} is using MixinOne.".format(self.name))


class MixinTwo(object):
    def print_name(self):
        print("{} is using MixinTwo.".format(self.name))


class Sub(object):

    def __new__(cls, *args, **kwargs):

        mixin = args[1]

        if mixin == 'one':
            bases = (MixinOne,) + cls.__bases__
        elif mixin == 'two':
            bases = (MixinTwo,) + cls.__bases__

        return object.__new__(type('Sub', bases, dict(cls.__dict__)))

    def __init__(self, name, mixin):

        print('In Sub.__init__')

        self.name = name
Run Code Online (Sandbox Code Playgroud)

唯一的问题似乎是__init__没有被调用,所以print_name方法不起作用.

  1. 我怎么__init__Sub火?

要么

  1. 有一个更好的方法吗?

tza*_*man 7

这是使用元类的一个巧妙的地方.您可以将自定义mixin-inclusion代码放在​​meta中,然后您的Sub类不需要具有样板:

class AutoMixinMeta(type):
    def __call__(cls, *args, **kwargs):
        try:
            mixin = kwargs.pop('mixin')
            name = "{}With{}".format(cls.__name__, mixin.__name__)
            cls = type(name, (mixin, cls), dict(cls.__dict__))
        except KeyError:
            pass
        return type.__call__(cls, *args, **kwargs)

class Sub(object):
    __metaclass__ = AutoMixinMeta

    def __init__(self, name):
        self.name = name
Run Code Online (Sandbox Code Playgroud)

现在您可以创建Sub对象并指定mixin,如下所示:

>>> s = Sub('foo', mixin=MixinOne)
>>> s.print_name()
foo is using MixinOne.
Run Code Online (Sandbox Code Playgroud)

它将自动从kwargs dict中撤出,因此该__init__方法可以完全不知道它的存在.


注意:在Python 3中,mixin声明语法发生了变化:

class Sub(metaclass = AutoMixinMeta):
    def __init__(self, name):
    self.name = name
Run Code Online (Sandbox Code Playgroud)


Eev*_*vee 3

  1. __init__仅当__new__返回类的实例时才被调用。您的即时类继承自 mixin 和SubParents,但不继承自Sub其自身。如果你设置它可能会起作用bases = (MixinOne, cls)

  2. 编写工厂函数(或类方法)而不是重载的Sub构造。更好的是,只需创建一些子类,而不是在运行时创建类。:)

  • 1. 有效。谢谢。2. 我有相当多的 mixins 和一些像 Sub 这样的类,我必须手动编写的子类数组并不能保证这个解决方案是实用的。 (2认同)