如何在实例化时有条件地将混合添加到当前 class 中?

How can I conditionally add in a mixin to the current class on instantiation?

我想要一个 class 可以根据传递给构造函数的参数添加混合。这是我试过的:

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. 有更好的方法吗?
  1. __init__ 仅在 __new__ returns class 的实例时调用。您的动态 class 继承自混入和 Sub 父级 ,但不是 Sub 本身。如果你设置 bases = (MixinOne, cls).

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

这是使用 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(metaclass = AutoMixinMeta):
    def __init__(self, name):
        self.name = name

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

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

它会自动从 kwargs 字典中拉出来,这样 __init__ 方法就可以完全不知道它的存在。


注意:Python2中的元类声明语法略有不同:

class Sub(object):
    __metaclass__ = AutoMixinMeta

    def __init__(self, name):
    self.name = name