• 你的第一个装饰器

    你的第一个装饰器

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

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