dil*_*ert 7 python class method-resolution-order
我发现自己处于一种不寻常的情况,我需要在运行时更改类的MRO.
代码:
class A(object):
def __init__(self):
print self.__class__
print "__init__ A"
self.hello()
def hello(self):
print "A hello"
class B(A):
def __init__(self):
super(B, self).__init__()
print "__init__ B"
self.msg_str = "B"
self.hello()
def hello(self):
print "%s hello" % self.msg_str
a = A()
b = B()
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,这失败了,因为__init__A 的方法(当从B调用时)调用B hello,它试图在属性存在之前访问它.
问题在于我可以做出的改变受到限制:
__init__我确实通过在运行时更改MRO来解决这个问题.简而言之,在B期间__init__,但在调用super之前__init__,MRO将被更改,以便首先搜索A的方法,从而调用A hello而不是B(因此失败).
问题是MRO是只读的(在类运行时).
还有其他方法可以实现吗?或者可能完全不同的解决方案(仍然尊重上述约束)?
dil*_*ert 11
如果您不受问题中提到的约束条件的约束,则建议使用其他答案.否则,我们需要前往mro hacks和metaclass land.
然而,这是在类创建时,而不是在对象创建时.稍微修改是必要的.
元类提供了mro我们重载的方法,在类创建(元类' __new__调用)期间调用该方法以生成__mro__属性.
该__mro__属性不是普通属性,其中:
__new__调用之前定义的但是,mro当更改类的基础时,似乎会重新计算(使用该方法).这构成了黑客的基础.
简单来说:
B)是使用元类(change_mro_meta)创建的.这个元类提供:
__mro__属性的类方法change_mro用于控制mro行为的class attribute()正如提到的,修改的一类,而的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()
Run Code Online (Sandbox Code Playgroud)
一些说明:
的hack_mro,fix_mro而recalc_mro方法是staticmethods到元类,但classmethods到类.它这样做,而不是多重继承,因为我想将mro代码组合在一起.
该mro方法本身通常返回默认值.在hack条件下,它将默认mro的第二个元素(直接父类)附加到mro,从而使父类在子类之前首先看到它自己的方法.
我不确定这个黑客的可移植性.它已经在Windows 7 64bit上运行的64位CPython 2.7.3上进行了测试.
别担心,我确信这不会在某个地方的生产代码中结束.