Python:强制装饰器继承?

Posted

技术标签:

【中文标题】Python:强制装饰器继承?【英文标题】:Python: Force decorator inheritance? 【发布时间】:2014-02-02 15:03:32 【问题描述】:

我正在使用相当大的 OOP 代码库,我想注入一些跟踪/日志记录。最简单的方法是在某些基类的某些方法周围引入一个装饰器,但不幸的是,装饰器不是继承的。

我确实尝试了以下方法:

def trace(fn):
    def wrapper(instance, *args, **kwargs):
        result = fn(instance, *args, **kwargs)
        # trace logic...
        return result
    return wrapper

class BaseClass(object):
    def __init__(self, ...):
        ...
        self.__call__ = trace(self.__call__)  # line added to end of method

...虽然 __call__ 方法 包装(我可以从将实例信息打印到控制台中看到),但 wrapper 函数没有按预期执行。 p>

我还简要地研究了使用基于 this answer 的元类,但它会立即破坏系统中使用自省的其他部分,所以我认为这是不行的。

有没有其他方法可以围绕继承自BaseClass 的类的__call__ 方法强制应用这些装饰器?

【问题讨论】:

所以您是说要将__call__ 包裹在BaseClass 及其所有子代中? @freakish:是的,有点。 __call__ in BaseClass 被所有子代(其中有很多)覆盖,因此在 __call__ 周围应用装饰器在 BaseClass 级别是在一个地方一致地添加跟踪的最简单方法。 这是否与您要解决的问题相同,基类上的装饰器继承到子类。 ***.com/questions/3782040/… 【参考方案1】:

为什么元编程会扰乱自省?也许您没有正确使用它?试试这个(假设 Python2.x):

class MyMeta(type):
    def __new__(mcl, name, bases, nmspc):
        if "__call__" in nmspc:
            nmspc["__call__"] = trace(nmspc["__call__"])
        return super(MyMeta, mcl).__new__(mcl, name, bases, nmspc)

class BaseClass(object):
    __metaclass__ = MyMeta

然后您可以简单地从BaseClass 继承,__call__ 将自动包装。

我不确定什么样的内省会打破这一点。除非 BaseClass 实际上 继承自 object 而是从实现自己的元的东西?但是您也可以通过强制 MyMeta 从该父级的元数据(而不是 type)继承来处理这种情况。

【讨论】:

这行得通;我认为我对__init__ 方法的使用导致内省失败,而__new__ 在正确的位置“切入”。 @unpluggd 这很奇怪,我认为__init__ 也可以。我会深入研究它。虽然在元编程方面我倾向于更频繁地使用__new__ 我认为您不需要在超类的__new__ 调用中将mcl 作为参数传递; super 已经绑定了该参数。 @user2357112 实际上你必须这样做。我不确定幕后会发生什么,但 Python 会抛出异常。这可能与 __new__ 在创建实际实例之前触发(在这种情况下“实例”表示“类”)这一事实有关。【参考方案2】:

你不能那样做......看看这里的东西: Python method resolution mystery

基本上。大多数特殊功能都在级别查找。初始化后对其进行猴子修补不会有任何效果。

也就是说,你应该可以这样做:

class BaseClass(object):
    __call__ = trace(object.__call__)
    def __init__(self, ...):
        ...

现在,它被包裹在 class 级别,所以它可以工作了。

编辑:这不是一个真正的答案。你想要的是这样的:

class BaseClass(object):
    @decorator
    __call__ = something

class SubClass(BaseClass):
    __call__ = something_else

在这种情况下...无论您为 BaseClass __call__ 方法添加什么,SubClass 方法都会覆盖它。这与装饰器与非装饰器无关,因为它是 functions (嗯,真的,属性,因为在 python 中并没有太大的区别)可以继承。装饰器看起来与函数分离,但实际上并非如此 - 在上面,装饰器所做的只是将 __call__ 的定义从 something 更改为 decorator(something)(仍然必须返回一个可调用的)。

然后,在子类中,将此函数重新分配给完全不同的东西。

您可以使用类装饰器,但正如您所注意到的那样,这些东西往往会破坏内省;他们将一个类变成一个返回类的函数。工厂功能的类似问题。

也许看看profile 模块?我知道它有一些较低级别的钩子用于跟踪函数调用等,您也可以使用它们(尽管对它们了解不多)。

【讨论】:

不幸的是,我的trace 版本不是分析,而是更多通过系统跟踪数据。 好吧,我的意思是分析器解决了一个类似的问题 - 它必须包装/跟踪所有函数调用,以便它可以存储每个调用的 start_time/stop_time。您需要做类似的事情,除了不是计时,您将记录它们和/或检查参数以跟踪数据流。

以上是关于Python:强制装饰器继承?的主要内容,如果未能解决你的问题,请参考以下文章

Python 装饰器强制显式传递参数

有没有办法在继承期间持久化装饰器?

作为基类一部分的 Python 装饰器不能用于装饰继承类中的成员函数

重写的方法是不是继承python中的装饰器?

Python 装饰器和继承

Python装饰器继承问题