《Python编程的术与道:Python语言进阶》视频课程
《Python编程的术与道:Python语言进阶》视频课程链接:https://edu.csdn.net/course/detail/28618
装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外的功能。装饰器的返回值也是一个函数/类对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。
它经常用于有切面(aspect)需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。
def div(a,b): print(a/b)
div(2,4)
0.5
def div(a,b): if a<b: a,b = b,a print(a/b)
div(2,4)
2.0
def div(a,b): print(a/b) def smart_div(func): def inner(a,b): if a<b: a,b = b,a return func(a,b) return inner
div_new = smart_div(div) div_new(2,4)
2.0
装饰器包装一个函数,修改其行为。
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper def say_whee(): print("Whee!")
say_whee = my_decorator(say_whee)
say_whee()
Something is happening before the function is called. Whee! Something is happening after the function is called.
Python允许通过@
符号以更简单的方式使用装饰器。 下面的示例与上面的装饰器示例完全相同:
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper @my_decorator def say_whee(): print("Whee!")
say_whee()
Something is happening before the function is called. Whee! Something is happening after the function is called.
因此,@my_decorator
只是表示say_whee = my_decorator(say_whee)
的一种简单方法。 这是将一个装饰器应用于一个函数的方法。
装饰器在运行期对函数进行一些外部功能的扩展。但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下比如测试时会导致一些问题。Python 通过 functool.wraps
为我们解决了这个问题:在编写装饰器时,在实现前加入 @functools.wraps(func)
可以保证装饰器不会对被装饰函数造成影响。主要是不改变使用装饰器原有函数的结构(如__name__
, __doc__
)
__name__
和__doc__
输出的内容def decorator(func): """this is decorator __doc__""" def wrapper(*args, **kwargs): """this is wrapper __doc__""" print("this is wrapper method") return func(*args, **kwargs) return wrapper @decorator def test(): """this is test __doc__""" print("this is test method") print("__name__: ", test.__name__) print("__doc__: ", test.__doc__)
__name__: wrapper __doc__: this is wrapper __doc__
对test()方法进行装饰时候,实际上是调用test = decorator(test)
返回的是wrapper方法的引用,也就是让test指向了wrapper方法,所以调用test.__name__
, 实际上是wrapper.__name__
,这可能会造成后面查找该方法的名字和注释的时候会得到装饰器的内嵌函数的名字和注释。
from functools import wraps def decorator(func): """this is decorator __doc__""" @wraps(func) def wrapper(*args, **kwargs): """this is wrapper __doc__""" print("this is wrapper method") return func(*args, **kwargs) return wrapper @decorator def test(): """this is test __doc__""" print("this is test method") print("__name__: ", test.__name__) print("__doc__: ", test.__doc__)
__name__: test __doc__: this is test __doc__
import functools def decorator(func): @functools.wraps(func) def wrapper_decorator(*args, **kwargs): # Do something before value = func(*args, **kwargs) # Do something after return value return wrapper_decorator
首先创建一个@timer
装饰器。 它将测量一个函数执行所需的时间并将时间打印到控制台。
import functools import time def timer(func): """Print the runtime of the decorated function""" @functools.wraps(func) def wrapper_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 def waste_some_time(num_times): for _ in range(num_times): sum([i**2 for i in range(10000)])
waste_some_time(1)
Finished 'waste_some_time' in 0.0049 secs
waste_some_time(999)
Finished 'waste_some_time' in 3.0510 secs