提问人:Strawbert 提问时间:8/28/2023 更新时间:8/28/2023 访问量:57
修饰 Python 类的所有函数,同时能够访问装饰器中的类属性
Decorate all functions of a Python class while being able to access the class attributes within the decorator
问:
我有一个类,并希望将装饰器应用于该类中的所有函数,而不必为每个函数添加函数装饰器。 我知道有像这里解释的解决方案 如何装饰类的所有函数,而无需为每个方法一遍又一遍地键入它? 将装饰器添加到整个类。但是,我需要能够访问装饰器中的所有类属性。
因此,像这样使用其他解决方案中的装饰器,我需要能够访问 f 中的所有 cls 属性
def function_decorator(orig_func):
def decorator(*args, **kwargs):
print("Decorating wrapper called for method %s" % orig_func.__name__)
result = orig_func(*args, **kwargs)
return result
return decorator
def class_decorator(decorator):
def decorate(cls):
# this doesn't work
# print(cls.name)
for attr in cls.__dict__:
if callable(getattr(cls, attr)):
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
@class_decorator(function_decorator)
class PersonWithClassDecorator:
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
me = PersonWithClassDecorator("Me")
me.print_name()
cls 变量的类型为 <class 'main.PersonWithClassDecorator>。
有没有人对如何实现这一目标有想法?我还研究了元类,但遇到了无法访问类属性的相同问题。任何帮助都非常感谢:)
答:
备选方案1
正如@Nullman建议的那样,您可以将装饰品移入__getattribute__
钩中。每当从类的实例访问任何属性(包括方法)时,都会调用此方法。
你可以直接在类中实现,也可以创建一个只包含装饰的 mixin。__getattribute__
from typing import Any
class Decorate:
def __getattribute__(self, item: str) -> Any:
val = super().__getattribute__(item)
if callable(val):
print("Name", self.name) # you can access the instance name here
return function_decorator(val)
return val
然后从 继承。Decorator
class PersonWithClassDecorator(Decorate): ...
这种简单的方法有两个缺点:
- 被硬编码到类中
function_decorator
Decorate
- dunder-methods like ,或者所有的运算符钩子都没有修饰(在内部,它们不是从实例访问的,而是从类访问的)
__init__
__str__
注意:如果您使用 (you should ;-) ),钩子将禁用对不存在属性的检测。要解决此问题,请将定义包装在一个块中。mypy
__getattribute__
__getattribute__
if not typing.TYPE_CHECKING
备选方案2
实际上可以访问实例属性。您可以相应地更改它并使用原始方法。function_decorator
from collections.abc import Callable
from typing import Any
def function_decorator(orig_function: Callable[..., Any]) -> Callable[..., Any]:
def decorator(self, *args: Any, **kwargs: Any) -> Any:
print(f"Decorating wrapper called for method {orig_func.__name__}: {self.name}")
return orig_function(self, *args, **kwargs)
return decorator
注意:我添加了类型提示,并用-strings替换了旧的基于-string的字符串格式。%
f
这有一个小问题:只在被调用之后才定义。您可以通过不同的方式处理此问题:self.name
__init__
getattr(self, 'name', None)
-self.name
在 init 之前None
- 不要装饰:
__init__
if orig_func.__name__ == '__init__': return orig_func
- 使用不同的装饰器
__init__
缺点:
- 您还必须处理和
@classmethod
@staticmethod
- 现在已耦合到您的类
function_decorator
评论
__getattribute__
self
name
PersonWithClassDecorator
"Me"