如何在Python中创建Mixin工厂?

时间:2012-01-31 21:16:12

标签: python metaprogramming multiple-inheritance mixins

我有许多类被其他类包装以添加新功能。

不幸的是,包装类没有为它们包装的类实现传递函数,因此包装器不能与原始类互换使用。

我想动态创建包含包装器和原始类的功能的类。

我的想法是创建一个混合类并使用工厂将其应用于现有类以动态创建新的双用途类。这应该允许我编写一次混合,并且混合类用于通过一个对象从混合中提供原始功能或增强功能。

这就是我要追求的事情:

class A:
    def __init__(self):
        self.name = 'A'

    def doA(self):
        print "A:", self.name


class B(A):
    def __init__(self):
        self.name = 'B'

    def doB(self):
        print "B:", self.name


class C(A):
    def __init__(self):
        self.name = 'C'

    def doC(self):
        print "C:", self.name


class D:
    def doD(self):
        print "D:", self.name


class BD(B,D):
    pass


def MixinFactory(name, base_class, mixin):
    print "Creating %s" % name
    return class(base_class, mixin)     # SyntaxError: invalid syntax

a, b, c, d, bd = A(), B(), C(), D(), BD()

bd2 = MixinFactory('BD2', B, D)()
cd = MixinFactory('CD', C, D)()

a.doA()     # A: A

b.doA()     # A: B
b.doB()     # B: B

c.doA()     # A: C
c.doC()     # C: C

bd.doA()    # A: B
bd.doB()    # B: B
bd.doD()    # D: B

bd2.doA()   # A: B
bd2.doB()   # B: B
bd2.doD()   # D: B

cd.doA()    # A: C
cd.doC()    # C: C
cd.doD()    # D: C

问题是很明显,你不能只从函数中返回一个类。忽略语法错误,上面的代码确实显示了我想要实现的目标。

我玩type()的三个参数变体,但无法使其起作用,所以我不确定这是否是正确的方法。

我认为在Python中创建这种类型的混合工厂是可能的,所以我需要了解什么才能实现它?


Niklas R评论时,问题this answerPython dynamic inheritance: How to choose base class upon instance creation?为我的查询提供了解决方案,但Ben的回答可以更好地解释原因。

2 个答案:

答案 0 :(得分:6)

实际上你可以从一个函数返回一个类。您的语法错误是您正在使用关键字,就像它是您可以调用的函数一样。

请记住,所有类块都是创建一个新类,然后将您选择的名称绑定到它(在当前范围内)。所以只需在你的函数中放一个类块,然后返回类!

def mixinFactory(name, base, mixin):
    class _tmp(base, mixin):
        pass
    _tmp.__name__ = name
    return _tmp

ejucovy所述,您也可以直接致电type

def mixinFactory(name, base, mixin):
    return type(name, (base, mixin), {})

这是有效的,因为它(通常)是一个类块实际上做的事情;它将您在类块中定义的所有名称收集到字典中,然后将类的名称,基类的元组和字典传递到type以构建新类。

但是,type只不过是默认元类。类是其他所有对象,是类的实例。大多数类都是type的实例,但是如果涉及另一个元类,则应该调用它而不是type,就像你不会调用object来创建一个你班上的新实例。

你的mixin(大概)没有定义元类,所以它应该与作为type的子类的任何元类兼容。所以你可以使用base的任何类:

def mixinFactory(name, base, mixin):
    return base.__class__(name, (base, mixin), {})

然而,S.Lott的评论看起来真的是最好的答案"对于这个问题,除非你的mixin工厂做的事情比创建一个新类更复杂。与任何提出的动态类创建变体相比,这更清晰,更少输入:

class NewClass(base, mixin):
    pass

答案 1 :(得分:4)

您可以在任何地方使用类声明,类声明可以引用子类化的变量。至于类名,它只是类对象的.__name__属性。所以:

def MixinFactory(name, base_class, mixin):
  print "Creating %s" % name
  class kls(base_class, mixin):
    pass
  kls.__name__ = name
  return kls

应该这样做。

对于单行,三参数type函数也应该起作用:

def MixinFactory(name, base_class, mixin):
  return type(name, (base_class, mixin), {})