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:强制装饰器继承?的主要内容,如果未能解决你的问题,请参考以下文章