我试图在Python中创建一个元类,在创建过程中根据创建实例时给定的参数动态更改类型的基类。在
简而言之,我有一个层次结构,C --> B --> A
但我想做的是,如果某些东西被传递给C
进行构造,我要动态地交换A
的其他实现。在
因为C是这个库的用户实现的,所以我不想强迫他们写一些初学者不懂的东西,所以我的计划是在B
内部实现魔法,它的存在只是为了将A
转移到适当的实现中。在
根据我对metaclasses和^{
class A(object):
pass
class Aimpl1(object):
def foo(self):
print "FOO"
class Aimpl2(object):
def foo(self):
print "BAR"
class AImplChooser(type):
def __call__(self, *args, **kwargs):
print "In call"
return super(AImplChooser,self).__call__(*args,**kwargs)
def __new__(cls, clsname, bases, dct):
print "Creating: " + clsname + ", " + ','.join([str(x) for x in bases])
return super(AImplChooser,cls).__new__(cls, clsname, bases, dct)
class B(A):
__metaclass__ = AImplChooser
def __init__(self, arg1, arg, arg3):
pass
class C(B):
def __init__(self, arg1, arg2=0, arg3=[]):
super(C, self).__init__(arg1, arg2, arg3)
c=C('')
print type(c)
print dir(type(c))
print c.__class__.__bases__
c.foo()
我的计划是根据B.__call__
的参数来转移B.__new__
内的碱基,但当然它们根本不会按这个顺序被调用,所以这不是一个选项。在
我想把__new__
全部删除,然后在__call__
内完成,但问题是,到那时对象已经存在了,所以更改基太晚了。在
关于类和元类,我缺少什么?有办法吗?在
更新:一种可能的替代方法是使用类修饰符来扮演当前的
B
角色:(不过这还需要一些工作)。在
因此,用户装饰他们的类而不是继承,并按常规编写
C
,但它的基将是一个适当的A
。。。在如果我理解正确的话,从一开始使用工厂函数并创建一个新的
^{pr2}$type
并使用适当的基来创建一个新的type
会更容易。。。在相当于:
我相信我已经成功地实现了您所要求的元类。我不确定这是否是最好的设计,但它是有效的。
C
的每个概念实例实际上都是C
的“专门化”实例,它派生自B
的一个专门化,它派生自一个专门化的A
类(这些A
类不需要以任何方式关联)。给定的C
专用化的所有实例都将具有相同的类型,但与具有不同专门化的实例的类型不同。继承的工作方式相同,专门化定义了单独的并行类树。在我的代码是:
首先,我们需要定义
A
类的专门化。这可以根据您的需要进行,但是在我的测试中,我使用了列表理解来在num
类变量中构建一组具有不同名称和不同值的类。在接下来,我们有一个“dummy”未专门化的}类的查找。如果您使用不同的方法来定义专用的
^{pr2}$A
类,它实际上只是一个元类可以挂接的地方。A
的元类AMeta
执行我在上面定义的列表中指定的{A
类,请更改AMeta._get_specialization
以找到它们。如果您愿意,甚至可以在这里按需创建A
的新专门化。在现在,我们来看看类}。这就是我们的子类的实际专业化发生的地方。元类的
B
及其元类{__call__
方法基于selector
参数,使用_get_specialization
方法来构建类的专用版本。_get_specialization
缓存其结果,因此在继承树的给定级别上,每个专门化只生成一个类。在如果需要的话,可以稍微调整一下(使用多个参数来计算
selector
),并且您可能希望将选择器传递给类构造函数,具体取决于它的实际情况。当前元类的实现只允许单一继承(一个基类),但它可能会被扩展以支持多重继承,以防您需要这种情况。在注意,虽然
B
类在这里是空的,但是您可以给它在每个专门化中出现的方法和类变量(作为浅拷贝)。在使用此设置,用户可以定义从}的各种专门化继承的一整套专门化类。在
B
继承的任意数量的C
类。在幕后,他们将真正定义从B
和{需要注意的是,C从来没有真正用作常规类。
C
实际上是一个工厂,它创建各种相关类的实例,而不是自己的实例。在但是,这里有一个不明显的行为:
如果希望非专用化的
B
和C
类型假装为其专门化的超类,可以将以下函数添加到BMeta
:这将说服内置的
isinstance
和issubclass
函数将B
和C
返回的实例视为其“factory”类的实例。在下面是我目前能收集到的最接近的信息:
它可以这样使用:
^{pr2}$它与您的设置在以下几个方面有所不同:
类C必须使用},那么新类将继承{},并且尝试在内部创建一个新类将再次使用这种魔力。通过使用
makeIt
类方法实例化,而不是直接用C(blah)
来实例化。这是为了避免无限循环。如果在B
中使用__new__
来处理委托,但是神奇地创建的具有交换基的新类必须继承原始的{__new__
并向动态创建的类添加一个“secret”属性并检查该属性以跳过魔术,也可以避免这种情况。B不从A继承,因此当C从B继承时,它也不会从A继承;而是从正确交换的实现基继承。
相关问题 更多 >
编程相关推荐