【问题标题】:dynamic inheritance in Python through a decorator通过装饰器在 Python 中动态继承
【发布时间】:2017-08-09 07:18:42
【问题描述】:

我找到了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),因为有很多分析类。我认为最好的方法是进行某种动态继承。现在我使用聚合将功能重定向到瞬态内的分析类。

【问题讨论】:

  • 重点是什么?如果您必须将基类放在装饰器调用中,您也可以将其填充为基类
  • 重点是 MyCode 应该是代码所期望的另一个类的包装器。因此,我不想使用聚合,而是想使用继承,这样我就不必将调用重定向到预期的类。

标签: python inheritance decorator python-decorators


【解决方案1】:

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

但是,这意味着该类已经设置了它的基础 - 通常不能更改这些基础。这意味着您必须在某些情况下重建装饰器代码中的类。

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

因此,可能有一种方法,但它并非易事。正如我在上面的评论中指出的那样,我想了解您希望通过此实现的目标,因为可以将 base 类放在类声明本身上,而不是在装饰器配置。

我已经制作了一个函数,如上所述,它创建一个新类,“克隆”原始类,并且可以重新构建所有使用 __class__super 的方法:它返回功能性的新类与原来的相同,但交换了碱基。如果按要求在装饰器中使用(包括装饰器代码),它将简单地更改类库。它不能处理修饰方法(classmethod 和 staticmethod 除外),并且不关心命名细节 - 例如方法的 qualnames 或 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

【讨论】:

  • 我已经根据实际情况更新了我的问题。
猜你喜欢
  • 2011-03-01
  • 2011-03-23
  • 2014-02-02
  • 2022-01-18
  • 2013-09-24
  • 2020-06-03
  • 2014-03-07
  • 2018-04-17
  • 2011-09-18
相关资源
最近更新 更多