如果您不受问题中提到的约束条件的约束,建议您提供其他答案。否则,我们需要踏上 mro hacks 和元类领域的旅程。
经过一番阅读,我发现您可以使用元类更改类的 mro。
然而,这是在类创建时,而不是在对象创建时。稍作修改是必要的。
元类提供了mro
我们重载的方法,该方法在类创建(元类的__new__
调用)期间调用以生成__mro__
属性。
该__mro__
属性不是普通属性,因为:
- 它是只读的
- 它是在元类调用之前定义的
__new__
mro
但是,当类的基数发生变化时,它似乎会被重新计算(使用该方法)。这构成了黑客攻击的基础。
简单来说:
- 子类 (
B
) 是使用元类 ( change_mro_meta
) 创建的。这个元类提供:
- 重载的 mro 方法
- 更改
__mro__
属性的类方法
change_mro
用于控制 mro 行为的类属性 ( )
如前所述,修改类的 mro不是__init__
线程安全的。
以下内容可能会打扰部分观众。建议观众酌情决定。
黑客:
class change_mro_meta(type):
def __new__(cls, cls_name, cls_bases, cls_dict):
out_cls = super(change_mro_meta, cls).__new__(cls, cls_name, cls_bases, cls_dict)
out_cls.change_mro = False
out_cls.hack_mro = classmethod(cls.hack_mro)
out_cls.fix_mro = classmethod(cls.fix_mro)
out_cls.recalc_mro = classmethod(cls.recalc_mro)
return out_cls
@staticmethod
def hack_mro(cls):
cls.change_mro = True
cls.recalc_mro()
@staticmethod
def fix_mro(cls):
cls.change_mro = False
cls.recalc_mro()
@staticmethod
def recalc_mro(cls):
# Changing a class' base causes __mro__ recalculation
cls.__bases__ = cls.__bases__ + tuple()
def mro(cls):
default_mro = super(change_mro_meta, cls).mro()
if hasattr(cls, "change_mro") and cls.change_mro:
return default_mro[1:2] + default_mro
else:
return default_mro
class A(object):
def __init__(self):
print "__init__ A"
self.hello()
def hello(self):
print "A hello"
class B(A):
__metaclass__ = change_mro_meta
def __init__(self):
self.hack_mro()
super(B, self).__init__()
self.fix_mro()
print "__init__ B"
self.msg_str = "B"
self.hello()
def hello(self):
print "%s hello" % self.msg_str
a = A()
b = B()
一些注意事项:
和方法是元类的静态方法hack_mro
,但类的类方法。它这样做了,而不是多重继承,因为我想将 mro 代码组合在一起。fix_mro
recalc_mro
该mro
方法本身通常返回默认值。在hack条件下,它将默认mro(直接父类)的第二个元素附加到mro,从而导致父类在子类之前首先看到自己的方法。
我不确定这个黑客的可移植性。它已在 Windows 7 64 位上运行的 64 位 CPython 2.7.3 上进行了测试。
别担心,我敢肯定这不会出现在某个地方的生产代码中。