装饰器的目的是什么(为什么要使用它们)?

时间:2018-10-01 14:46:15

标签: python python-3.x

我一直在学习和尝试装饰器。我了解它们的作用:它们允许您在不更改现有功能的情况下向现有功能添加功能,从而编写模块化代码。

我发现了一个很好的线索,它通过在这里解释所有的来龙去脉确实帮助我学习了如何做:How to make a chain of function decorators?

但是此线程(以及我看过的其他资源)缺少的是为什么,我们需要装饰器语法吗?为什么需要嵌套函数来创建装饰器?为什么我不能只使用一个现有功能,编写另一个具有某些附加功能的功能,然后将第一个功能馈入第二个功能以使其做其他事情?

是因为这是惯例吗?我想念什么?我认为我的经验不足。

3 个答案:

答案 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都具有用于注释的不同颜色语法,这使它更易于理解和组织代码。

所以,我想可能仅仅是因为经验不足;如果您开始使用它们,您将自动开始知道在哪里使用它们。