Как восстановить мро класса с учетом его основ? - PullRequest
0 голосов
/ 15 сентября 2018

Предположим, что мы реализуем метакласс, который должен знать порядок разрешения метода до того, как будет создан экземпляр класса.

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

Есть ли встроенный способ вычисления mro, это иной способ, чем переопределение алгоритма C3 ?

1 Ответ

0 голосов
/ 20 сентября 2018

Более простая вещь - просто создать временный класс, извлечь его __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)

(Это работает для базовых случаев, которые я пробовал в интерактивном режиме - но могут быть сложные крайние случаи, не покрытые, с несколькимиметаклассы и прочее)

...