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

时间:2015-01-28 23:05:54

标签: python class mixins

我希望有一个基于传递给构造函数的参数添加mixin的类。这就是我尝试过的:

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

唯一的问题似乎是__init__没有被调用,因此print_name方法无效。

  1. 如何__init__ Sub开火?
    1. 有更好的方法吗?

2 个答案:

答案 0 :(得分:6)

这是使用元类的一个巧妙的地方。您可以将自定义混合包含代码放在元数据中,然后您的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

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

>>> s = Sub('foo', mixin=MixinOne)
>>> s.print_name()
foo is using MixinOne.

它会自动退出kwargs dict,因此__init__方法可以完全不知道它的存在。


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

class Sub(metaclass = AutoMixinMeta):
    def __init__(self, name):
    self.name = name

答案 1 :(得分:1)

    仅当__init__返回类的实例时,才会调用
  1. __new__。您的即时课程继承自mixin和Sub 父母,但不是来自Sub本身。如果您设置bases = (MixinOne, cls)

  2. ,它可能会起作用
  3. 编写工厂函数(或classmethod),而不是重载Sub的构造。更好的是,只需创建一些子类,而不是在运行时创建类。 :)