我一直在学习和尝试装饰器。我了解它们的作用:它们允许您在不更改现有功能的情况下向现有功能添加功能,从而编写模块化代码。
我发现了一个很好的线索,它通过在这里解释所有的来龙去脉确实帮助我学习了如何做:How to make a chain of function decorators?
但是此线程(以及我看过的其他资源)缺少的是为什么,我们需要装饰器语法吗?为什么需要嵌套函数来创建装饰器?为什么我不能只使用一个现有功能,编写另一个具有某些附加功能的功能,然后将第一个功能馈入第二个功能以使其做其他事情?
是因为这是惯例吗?我想念什么?我认为我的经验不足。
答案 0 :(得分:3)
来自PEP 318 -- Decorators for Functions and Methods(我非常强调):
动机
将转换应用于的当前方法 函数或方法将实际转换放在函数之后 身体。对于大型功能,这将 从函数其余部分的定义中得出的函数行为 外部接口。例如:
def foo(self): perform method operation foo = classmethod(foo)
对于较长的方法,这变得可读性较低。似乎小于pythonic的名字 该功能在概念上是单一的,它是三次的 声明。 此问题的解决方案是移动转换 方法的名称更接近方法本身的声明。目的 新语法是替换
def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)
另一种将修饰符放置在函数的声明中的方法:
@classmethod @synchronized(lock) def foo(cls): pass
答案 1 :(得分:1)
装饰器为何有用的一个很好的例子是numba包。
它提供了一个装饰器来加速Python函数:
@jit
def my_slow_function():
# ...
@jit
装饰器执行了一些非常神奇且复杂的操作-将整个功能(包括其部分功能)编译为机器代码。如果Python没有装饰器,则您必须自己用机器代码编写函数……或更严重的是,语法应如下所示:
def my_slow_function():
# ...
my_slow_function = jit(my_slow_function)
装饰器语法的目的是使第二个示例更好。这只是语法糖,因此您不必键入三次函数名称。
答案 2 :(得分:1)
我将尝试使其简单,并举例说明。 我经常要做的一件事是衡量我构建API并将其发布到AWS上所花费的时间。
由于这是一个非常常见的用例,因此我为其创建了一个装饰器。
def log_latency():
def actual_decorator(f):
@wraps(f)
def wrapped_f(*args, **kwargs):
t0 = time()
r = f(*args, **kwargs)
t1 = time()
async_daemon_execute(public_metric, t1 - t0, log_key)
return r
return wrapped_f
return actual_decorator
现在,如果有任何方法可以测量延迟,我只需使用所需的装饰器对其进行注释。
@log_latency()
def batch_job_api(param):
pass
假设您要编写一个安全的API,该API仅在发送带有特定值的标头时才起作用,然后您可以为其使用装饰器。
def secure(f):
@wraps(f)
def wrapper(*args, **kw):
try:
token = request.headers.get("My_Secret_Token")
if not token or token != "My_Secret_Text":
raise AccessDenied("Required headers missing")
return f(*args, **kw)
return wrapper
现在就写
@secure
def my_secure_api():
pass
我也一直在使用上述语法来处理API特有的异常,如果某个方法需要数据库交互而不是获取连接,则使用@session装饰器,它告诉该方法将使用数据库连接,而您不需要自己动手。
我显然可以通过编写一个函数来检查标题或打印API在AWS上花费的时间来避免这种情况,但这看起来有点丑陋且不直观。
对此没有约定,至少我不知道它们,但是它确实使代码更易读和易于管理。 大多数IDE都具有用于注释的不同颜色语法,这使它更易于理解和组织代码。
所以,我想可能仅仅是因为经验不足;如果您开始使用它们,您将自动开始知道在哪里使用它们。