我发现自己处于一种不寻常的情况,需要在运行时更改类的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()

正如预期的那样,这会失败,因为a的__init__方法(当从b调用时)调用b的hello方法,该方法试图在属性存在之前访问它。
问题是,我所做的更改受到限制:
B必须是A子类
A不能更改
A和B都需要hello方法
b无法在调用super之前初始化其他属性
我在概念上解决了这个问题,在运行时更改了MRO。简言之,在b的__init__期间,但在调用super__init__之前,mro会被更改,以便首先搜索a的方法,从而调用a的__init__而不是b的(因此失败)。
问题是MRO是只读的(在类运行时)。
是否有其他方法来实现这一点?或者可能是一个完全不同的解决方案(仍然遵守上述限制)?

最佳答案

如果您不受问题中提到的约束条件的约束,则建议使用其他提供的答案。否则,我们需要去MRO黑客和元类土地旅行。
在阅读了一些文章之后,我发现您可以使用元类来change the mro类。
然而,这是在类创建时,而不是在对象创建时。轻微的修改是必要的。
元类提供了我们重载的mro方法,该方法在类创建期间调用(元类的__new__调用),以生成__mro__属性。
__mro__属性不是普通属性,因为:
它是只读的
它是在元类“__new__调用之前定义的
但是,当类的基被更改时,它似乎被重新计算(使用mro方法)。这是黑客攻击的基础。
简而言之:
子类(B)是使用元类(change_mro_meta)创建的。这个元类提供了:
重载的MRO方法
类方法来更改__mro__属性
用于控制MRO行为的类属性(change_mro
作为mentioned,在类的__init__中修改它的mro是不安全的。
以下内容可能会干扰某些观众。建议观众自行决定。
黑客:

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_mrofix_mrorecalc_mro方法是元类的静态方法,而类方法是类的静态方法。它做到了这一点,而不是多重继承,因为我想将MRO代码分组在一起。
mro方法本身通常返回默认值。在hack条件下,它将默认mro(直接父类)的第二个元素附加到mro,从而使父类在子类之前首先看到自己的方法。
我不确定这个黑客的可移植性。它在64位CPython2.7.3上进行了测试,运行在Windows7 64位上。
别担心,我相信这不会在某个地方出现在生产代码中。

10-04 09:51