你的第一个装饰器

在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序:

  1. def a_new_decorator(a_func):
  2. def wrapTheFunction():
  3. print("I am doing some boring work before executing a_func()")
  4. a_func()
  5. print("I am doing some boring work after executing a_func()")
  6. return wrapTheFunction
  7. def a_function_requiring_decoration():
  8. print("I am the function which needs some decoration to remove my foul smell")
  9. a_function_requiring_decoration()
  10. #outputs: "I am the function which needs some decoration to remove my foul smell"
  11. a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
  12. #now a_function_requiring_decoration is wrapped by wrapTheFunction()
  13. a_function_requiring_decoration()
  14. #outputs:I am doing some boring work before executing a_func()
  15. # I am the function which needs some decoration to remove my foul smell
  16. # I am doing some boring work after executing a_func()

你看明白了吗?我们刚刚应用了之前学习到的原理。这正是python中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用 @ 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:

  1. @a_new_decorator
  2. def a_function_requiring_decoration():
  3. """Hey you! Decorate me!"""
  4. print("I am the function which needs some decoration to "
  5. "remove my foul smell")
  6. a_function_requiring_decoration()
  7. #outputs: I am doing some boring work before executing a_func()
  8. # I am the function which needs some decoration to remove my foul smell
  9. # I am doing some boring work after executing a_func()
  10. #the @a_new_decorator is just a short way of saying:
  11. a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

希望你现在对 Python 装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题:

  1. print(a_function_requiring_decoration.__name__)
  2. # Output: wrapTheFunction

这并不是我们想要的!Ouput 输出应该是 “a_function_requiring_decoration”。这里的函数被 warpTheFunction 替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是 Python 提供给我们一个简单的函数来解决这个问题,那就是 functools.wraps。我们修改上一个例子来使用 functools.wraps

  1. from functools import wraps
  2. def a_new_decorator(a_func):
  3. @wraps(a_func)
  4. def wrapTheFunction():
  5. print("I am doing some boring work before executing a_func()")
  6. a_func()
  7. print("I am doing some boring work after executing a_func()")
  8. return wrapTheFunction
  9. @a_new_decorator
  10. def a_function_requiring_decoration():
  11. """Hey yo! Decorate me!"""
  12. print("I am the function which needs some decoration to "
  13. "remove my foul smell")
  14. print(a_function_requiring_decoration.__name__)
  15. # Output: a_function_requiring_decoration

现在好多了。我们接下来学习装饰器的一些常用场景。

蓝本规范:

  1. from functools import wraps
  2. def decorator_name(f):
  3. @wraps(f)
  4. def decorated(*args, **kwargs):
  5. if not can_run:
  6. return "Function will not run"
  7. return f(*args, **kwargs)
  8. return decorated
  9. @decorator_name
  10. def func():
  11. return("Function is running")
  12. can_run = True
  13. print(func())
  14. # Output: Function is running
  15. can_run = False
  16. print(func())
  17. # Output: Function will not run

注意:@wraps 接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

使用场景

现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

授权(Authorization)

装饰器能有助于检查某个人是否被授权去使用一个 web 应用的端点(endpoint)。它们被大量使用于 Flask 和 Django web 框架中。这里是一个例子来使用基于装饰器的授权:

  1. from functools import wraps
  2. def requires_auth(f):
  3. @wraps(f)
  4. def decorated(*args, **kwargs):
  5. auth = request.authorization
  6. if not auth or not check_auth(auth.username, auth.password):
  7. authenticate()
  8. return f(*args, **kwargs)
  9. return decorated

日志(Logging)

日志是装饰器运用的另一个亮点。这是个例子:

  1. from functools import wraps
  2. def logit(func):
  3. @wraps(func)
  4. def with_logging(*args, **kwargs):
  5. print(func.__name__ + " was called")
  6. return func(*args, **kwargs)
  7. return with_logging
  8. @logit
  9. def addition_func(x):
  10. """Do some math."""
  11. return x + x
  12. result = addition_func(4)
  13. # Output: addition_func was called

我敢肯定你已经在思考装饰器的一个其他聪明用法了。