简体   繁体   English

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

[英]dynamic inheritance in Python through a decorator

I found this post where a function is used to inherit from a class: 我发现这篇文章中使用了一个函数来继承一个类:

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

I would like to do something similar, but with a decorator, something like: 我想做类似的事情,但是要用一个装饰器,例如:

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

Is this possible? 这可能吗?

UPDATE UPDATE

Say you have a class Analysis that is used throughout your code. 假设您有一个在整个代码中都使用的Analysis类。 Then you realize that you want to use a wrapper class Transient that is just a time loop on top of the analysis class. 然后,您意识到要使用包装类Transient ,该包装类只是分析类顶部的时间循环。 If in the code I replace the analysis class, but Transient(Analysis) everything breaks because an analysis class is expected, and thus all its attributes. 如果在代码中我替换了分析类,但是Transient(Analysis)一切都中断了,因为需要一个分析类,因此也需要其所有属性。 The problem is that I can't just get to define class Transient(Analysis) in this way because there are plenty of analysis classes. 问题是我不能仅仅以这种方式定义class Transient(Analysis) ,因为有很多分析类。 I thought the best way to do this would be to have some sort of dynamic inheritance. 我认为最好的方法是进行某种动态继承。 Right now I use aggregation to redirect the functionality to the analysis class inside transient. 现在,我使用聚合将功能重定向到瞬态内部的分析类。

A class decorator actually gets the class already built - and instantiated (as a class object). 一个类装饰器实际上获取已经构建的类-并实例化(作为类对象)。 It can perform changes on it's dict, and even wrap its methods with other decorators. 它可以对字典进行更改,甚至可以将其方法与其他装饰器包装在一起。

However, it means the class already has its bases set - and these can't be ordinarily changed. 但是,这意味着该类已经设置了基数-并且这些基数通常无法更改。 That implies you have to, in some ay rebuild the class inside the decorator code. 这意味着您必须在装饰器代码中重新构建类。

However, if the class'methods make use of parameterless super or __class__ cell variable, those are already set in the member functions (that in Python 3 are the same as unbound methods) you can't just create a new class and set those methods as members on the new one. 但是,如果类的方法使用无参数的super__class__单元格变量,则这些变量已在成员函数中设置(在Python 3中与未绑定方法相同),您不能只是创建一个新类并设置这些方法作为新成员的成员。

So, there might be a way, but it will be non-trivial. 因此,也许有一种方法,但这将是不平凡的。 And as I pointed out in the comment above, I d like to understand what you'd like to be able to achieve with this, since one could just put the base class on the class declaration itself, instead of using it on the decorator configuration. 正如我在上面的评论中指出的那样,我想了解您希望通过此方法实现什么,因为可以将base类放在类声明本身上,而不是在装饰器配置上使用它。

I've crafted a function that, as described above, creates a new class, "clonning" the original and can re-build all methods that use __class__ or super : it returns the new class which is functionally identical to the orignal one, but with the bases exchanged. 我精心设计了一个函数,如上所述,它创建了一个新类,“克隆”原始类,并可以重建使用__class__super所有方法:它返回的新类在功能上与原始类相同,但是与基地交换。 If used in a decorator as requested (decorator code included), it will simply change the class bases. 如果按要求在装饰器中使用(包括装饰器代码),它将仅更改类的基数。 It can't handle decorated methods (other than classmethod and staticmethod), and don't take care of naming details - such as qualnames or repr for the methods. 它不能处理修饰的方法(除classmethod和staticmethod之外),并且不关心命名细节-例如方法的qualname或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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM