代码之家  ›  专栏  ›  技术社区  ›  Olivier Melançon iacob

如何根据类的基恢复类的mro?

  •  3
  • Olivier Melançon iacob  · 技术社区  · 6 年前

    假设我们正在实现一个元类,它需要知道 方法解析顺序

    class Meta(type):
        def __new__(cls, name, bases, namespace):
            mro = ...
    

    有没有一个内置的方法来计算 mro公司 C3 algorithm ?

    1 回复  |  直到 6 年前
        1
  •  1
  •   jsbueno    6 年前

    更简单的方法是创建一个临时类,提取它的 __mro__ ,计算您的东西,然后创建真正的元类:

    class Meta(type):
        def __new__(metacls, name, bases, namespace):
            tmp_cls = super().__new__(metacls, name, bases, namespace)
            mro = tmp_cls.__mro__
            del tmp_cls  # Not actually needed, just to show you are done with it.
            ...
            # do stuff
            ...
            new_class = super().__new__(metacls, name, bases, namespace)
            ...
            return new_class
    

    假设这是不可能的,因为对层次结构上的一些超类的元类有疯狂的副作用-那么同样的想法,但是在做之前将基中的类克隆到“存根”类-但是可能的是,重新实现C3算法比这更容易-当然更有效,因为对于每个类,您将创建N**2个存根超类,其中N是类层次结构的深度(如果您选择此路由,则可以缓存)。

    不管怎样,代码可能是:

    stub_cache = {object: object}
    
    def get_stub_class(cls):
        # yields an mro-equivalent with no metaclass side-effects.
        if cls is object:
            return object
        stub_bases = []
        for base in cls.__bases__:
            stub_bases.append(get_stub_class(base))
        if cls not in stub_cache:
            stub_cache[cls] = type(cls.__name__, tuple(stub_bases), {})
        return stub_cache[cls]
    
    def get_future_mro(name, bases):
        stub_bases = tuple(get_stub_class(base) for base in bases)
        stub_cls = type(name, stub_bases, {})
        reversed_cache = {value:key for key, value in stub_cache.items()}
        return [reversed_cache[mro_base] for mro_base in  stub_cls.__mro__[1:]]
    
    class Meta(type):
        def __new__(metacls, name, bases, namespace):
            mro = get_future_mro(name, bases)
            print(mro)
            return super().__new__(metacls, name, bases, namespace)
    

    (这个东西适用于我在交互模式下尝试过的基本情况-但是可能有复杂的边缘情况没有涵盖,有多个元类等等)