假设我们正在实现一个元类,在实例化类之前需要知道方法解析顺序。
class Meta(type):
def __new__(cls, name, bases, namespace):
mro = ...
有没有一种内置方法来计算 mro,这是除了重新实现 C3 算法之外的一种方法?
更简单的事情是创建一个临时类,提取它的
__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)
(这个东西适用于我在交互模式下尝试过的基本情况 - 但可能存在未涵盖的复杂边缘情况,具有多个元类等)
原来
functools
模块有一个实现C3线性化算法的_c3_merge
函数:
import functools
def compute_mro(*bases):
return functools._c3_merge(base.mro() for base in bases)
# Example:
class F: pass
class E: pass
class D: pass
class C(D,F): pass
class B(D,E): pass
class A(B,C): pass
print(compute_mro(B, C)) # [B, C, D, E, F, object]
print(A.mro()) # [A, B, C, D, E, F, object]