Динамическое наследование в Python через декоратор

Я нашел этот пост, где функция используется для наследования от класса:

def get_my_code(base):
    class MyCode(base):
        def initialize(self):
          ...
    return MyCode
my_code = get_my_code(ParentA)

Я хотел бы сделать что-то подобное, но с декоратором, что-то вроде:

@decorator(base)
class MyClass(base):
   ...

Это возможно?

ОБНОВИТЬ

Скажи, у тебя есть класс Analysis это используется во всем вашем коде. Затем вы понимаете, что хотите использовать класс-оболочку Transient это просто временная петля поверх класса анализа. Если в коде я заменяю класс анализа, но Transient(Analysis) все ломается, потому что ожидается анализ класса и, следовательно, все его атрибуты. Проблема в том, что я не могу просто определить class Transient(Analysis) таким образом, потому что есть много классов анализа. Я думал, что лучший способ сделать это - иметь какое-то динамическое наследование. Прямо сейчас я использую агрегацию, чтобы перенаправить функциональность в класс анализа внутри переходного процесса.

1 ответ

Декоратор класса фактически получает класс, уже созданный и созданный (как объект класса). Он может вносить изменения в свою информацию и даже обернуть свои методы другими декораторами.

Тем не менее, это означает, что класс уже имеет свои установленные базы - и они не могут быть изменены обычно. Это подразумевает, что вы должны каким-то образом перестроить класс внутри кода декоратора.

Однако, если методы класса используют без параметров super или же __class__ переменная ячейки, они уже установлены в функциях-членах (которые в Python 3 совпадают с несвязанными методами), вы не можете просто создать новый класс и установить эти методы в качестве членов нового.

Таким образом, может быть способ, но он будет нетривиальным. И, как я указал в комментарии выше, я хотел бы понять, чего бы вы хотели достичь, поскольку можно просто поставить base class в самом объявлении класса, вместо того, чтобы использовать его в конфигурации декоратора.

Я создал функцию, которая, как описано выше, создает новый класс, "клонирует" оригинал и может пересобрать все методы, которые используют __class__ или же super: возвращает новый класс, функционально идентичный классу оригинала, но с замененными базами. Если используется в декораторе по запросу (включая код декоратора), он просто изменит основы классов. Он не может обрабатывать декорированные методы (кроме classmethod и staticmethod) и не заботится о деталях именования - таких как квалификационные имена или repr для методов.

from types import FunctionType

def change_bases(cls, bases, metaclass=type):
    class Changeling(*bases, metaclass=metaclass):
        def breeder(self):
            __class__  #noQA

    cell = Changeling.breeder.__closure__
    del Changeling.breeder

    Changeling.__name__ = cls.__name__

    for attr_name, attr_value in cls.__dict__.items():
        if isinstance(attr_value, (FunctionType, classmethod, staticmethod)):
            if isinstance(attr_value, staticmethod):
                func = getattr(cls, attr_name)
            elif isinstance(attr_value, classmethod):
                func = attr_value.__func__
            else:
                func = attr_value
            # TODO: check if func is wrapped in decorators and recreate inner function.
            # Although reaplying arbitrary decorators is not actually possible -
            # it is possible to have a "prepare_for_changeling" innermost decorator
            # which could be made to point to the new function.
            if func.__closure__ and func.__closure__[0].cell_contents is cls:
                franken_func = FunctionType(
                    func.__code__,
                    func.__globals__,
                    func.__name__,
                    func.__defaults__,
                    cell
                )
                if isinstance(attr_value, staticmethod):
                    func = staticmethod(franken_func)
                elif isinstance(attr_value, classmethod):
                    func = classmethod(franken_func)
                else:
                    func = franken_func
                setattr(Changeling, attr_name, func)
                continue
        setattr(Changeling, attr_name, attr_value)

    return Changeling


def decorator(bases):
    if not isinstance(base, tuple):
        bases = (bases,)
    def stage2(cls):
        return change_bases(cls, bases)
    return stage2
Другие вопросы по тегам