defmy_decorator(func): defwrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper
defsay_whee(): print("Whee!")
say_whee = my_decorator(say_whee)
猜测一下调用say_whee()时会发生什么吗?试一试:
1 2 3 4
>>> say_whee() Something is happening before the function is called. Whee! Something is happening after the function is called.
defmy_decorator(func): defwrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper
>>> greet("World") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: wrapper_do_twice() takes 0 positional arguments but 1 was given
defdecorator(func): @functools.wraps(func) defwrapper_decorator(*args, **kwargs): # Do something before value = func(*args, **kwargs) # Do something after return value return wrapper_decorator
这个公式是一个很好的样板模板,用于构建更复杂的装饰器。
在后面的示例中,我们假设这些装饰器也保存在decorators.py文件中。
计时函数
让我们从创建 @timer 装饰器开始。它将测量函数执行所需的时间,并将函数持续时间打印到控制台。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import functools import time
deftimer(func): """Print the runtime of the decorated function""" @functools.wraps(func) defwrapper_timer(*args, **kwargs): start_time = time.perf_counter() # 1 value = func(*args, **kwargs) end_time = time.perf_counter() # 2 run_time = end_time - start_time # 3 print(f"Finished {func.__name__!r} in {run_time:.4f} secs") return value return wrapper_timer
@timer defwaste_some_time(num_times): for _ in range(num_times): sum([i**2for i in range(10000)])
defdebug(func): """Print the function signature and return value""" @functools.wraps(func) defwrapper_debug(*args, **kwargs): args_repr = [repr(a) for a in args] # 1 kwargs_repr = [f"{k}={v!r}"for k, v in kwargs.items()] # 2 signature = ", ".join(args_repr + kwargs_repr) # 3 print(f"Calling {func.__name__}({signature})") value = func(*args, **kwargs) print(f"{func.__name__!r} returned {value!r}") # 4 return value return wrapper_debug
@debug defmake_greeting(name, age=None): if age isNone: returnf"Howdy {name}!" else: returnf"Whoa {name}! {age} already, you are growing up!"
注意@debug装饰器如何打印make_greeting()函数的签名和返回值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
>>> make_greeting("Benjamin") Calling make_greeting('Benjamin') 'make_greeting' returned 'Howdy Benjamin!' 'Howdy Benjamin!'
>>> make_greeting("Richard", age=112) Calling make_greeting('Richard', age=112) 'make_greeting' returned 'Whoa Richard! 112 already, you are growing up!' 'Whoa Richard! 112 already, you are growing up!'
>>> make_greeting(name="Dorrisile", age=116) Calling make_greeting(name='Dorrisile', age=116) 'make_greeting' returned 'Whoa Dorrisile! 116 already, you are growing up!' 'Whoa Dorrisile! 116 already, you are growing up!'
>>> globals() {..., # Lots of variables not shown here. 'say_hello': <function say_hello at 0x7f768eae6730>, 'be_awesome': <function be_awesome at 0x7f768eae67b8>, 'randomly_greet': <function randomly_greet at 0x7f768eae6840>}
deflogin_required(func): """Make sure user is logged in before proceeding""" @functools.wraps(func) defwrapper_login_required(*args, **kwargs): if g.user isNone: return redirect(url_for("login", next=request.url)) return func(*args, **kwargs) return wrapper_login_required