Python中的动态继承通过装饰器

时间:2017-03-17 16:20:57

标签: python inheritance decorator python-decorators

我找到了this post,其中一个函数用于从类继承:

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 个答案:

答案 0 :(得分:0)

类装饰器实际上获取已经构建的类 - 并实例化(作为类对象)。它可以对它的dict执行更改,甚至可以将其方法与其他装饰器一起包装。

然而,这意味着该类已经设置了基础 - 这些通常不会改变。这意味着你必须在某些方面重建装饰器代码中的类。

但是,如果类的方法使用无参数super__class__单元格变量,那些已经在成员函数中设置(在Python 3中与未绑定方法相同),你可以不要只创建一个新类并将这些方法设置为新类的成员。

所以,可能有一种方法,但这将是非平凡的。正如我在上面的评论中指出的那样,我想了解你希望能够实现的目标,因为可以将base类放在类声明本身上,而不是使用它在装饰者配置上。

我已经精心设计了一个函数,如上所述,创建了一个新类,“克隆”原始函数,并可以重新构建使用__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