python装饰器(fuctional decorators)简单来说就是修改其他函数的函数。
这样的函数需要满足两个个条件:
1、不能修改原函数的源代码
2、不能改变原函数的调用方式
需要达到的效果:增加函数的功能
假设,我们已经定义了一个函数
import time
def test():
time.sleep(2)
print(‘The is test‘)
现在需要为这个程序添加一个功能:显示该程序运行的时间
理解装饰器的难点在于:
1、理解python一切皆为对象的含义
2、高阶函数
3、嵌套函数
这里只是简单看一下,理解函数即变量即可。后面学习深入会有更深入的理解。
1 >>> def say(name): 2 ... return ‘hello ‘+ name 3 ... 4 >>> print(say()) 5 >>> print(say(‘Michael‘)) 6 hello Michael 7 >>> 8 # 我们可以将函数名作为变量赋值给另一个变量,换句话说变量可以指向函数 9 >>> greet=say 10 >>> print(greet()) 11 >>> print(greet(‘Bob‘)) 12 hello Bob 13 # 变量删除,对象也就不存在了 14 >>> del say 15 >>> say(‘Jack‘) 16 Traceback (most recent call last): 17 File "<stdin>", line 1, in <module> 18 NameError: name ‘say‘ is not defined 19 >>>
高阶函数有两类:
a.把一个函数名当作实参传给另外一个函数
b.返回值中包含函数名
python一切皆为对象,实际上函数名就是函数的地址,也可以认为,是函数的一个标签。这样就可以把函数名作为一个参数传给另一个函数,在另一个函数里做一些操作,就可以在不修改源代码的基础上,为函数增加一些功能。
1 import time 2 def test(): 3 print(‘This is test‘) 4 5 def deco(func): 6 start_t = time.time() 7 func() 8 end_t = time.time() 9 print(‘the func %s run time is %s‘ % (func, end_t-start_t)) 10 11 deco(test)
在第11行处,把test作为实参传给形参func,在第7行处就是对形参的调用。func传入的是函数的地址,func()就是执行这个函数,这段代码运行的结果就是:
This is test the func <function test at 0x00000195C816EAE8> run time is 0.0
那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名
1 def test(): 2 time.sleep(3) 3 print(‘This is test‘) 4 5 def deco(func): 6 print(‘This is deco‘) 7 return func 8 9 test = deco(test) 10 test()
这种方式虽然可以不改变调用方式,但是无法完成计时功能,运算结果
This is deco This is test
1 import time 2 def timer(func): 3 def deco(): 4 start = time.time() 5 func() 6 stop = time.time() 7 print(‘The func running time is %s‘ % (stop - start)) 8 return deco 9 10 def test(): 11 time.sleep(2) 12 print("This is test") 13 14 test = timer(test) 15 test()
在第14行处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。
然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。那么在第15行处调用的实际上是deco()。执行结果
This is test The func running time is 2.000332832336426
那么通俗一点的理解就是:
把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来
,打开看看(调用)。
根据以上分析,装饰器在装饰时,需要在每个函数前面加上:
test = timer(test)
显然有些麻烦,Python提供了一种语法糖,即:
@timer
这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。
以上为无参形式。
1 import time 2 3 def decorator(func): 4 def wapper():#若被装饰函数含参,传到wapper即可 5 start_t = time.time() 6 func() 7 stop_t = time.time() 8 print(‘the func running time is %s‘ % (stop_t-start_t)) 9 return wapper 10 11 @decorator #test1=decorator(test1) =wapper大盒子替换到中盒子 12 def test():#表面是小盒子,实际上是中盒子 13 time.sleep(3) 14 print(‘in the test1‘)
执行结果:
in the test1 the func running time is 3.0000483989715576
对于一个实际问题,往往是有参数的,如果要在#8处,给被修饰函数加上参数,显然这段程序会报错的。错误原因是test()在调用的时候缺少了一个位置参数的。而我们知道test = func = deco,因此test()=func()=deco() ,那么当test(parameter)有参数时,就必须给func()和deco()也加上参数,为了使程序更加有扩展性,因此在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。
1 improt time 2 3 def timer(func) 4 def deco(*args, **kwargs): 5 start = time.time() 6 func(*args, **kwargs) 7 stop = time.time() 8 print(stop-start) 9 return deco 10 11 @timer 12 def test(parameter): 13 time.sleep(2) 14 print("test is running!") 15 test()
test()返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:
def timer(func) def deco(*args, **kwargs): start = time.time() res = func(*args, **kwargs)#9 stop = time.time() print(stop-start) return res#10 return deco
1 improt time 2 3 def timer(func) 4 def deco(*args, **kwargs): #含参的test()函数 5 start = time.time() 6 res = func(*args, **kwargs) 7 stop = time.time() 8 print(stop-start) 9 return res #函数带有返回值 10 return deco 11 12 @timer 13 def test(parameter): 14 time.sleep(2) 15 print("test is running!") 16 return "Returned value" 17 test()
又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:
@decorator(parameter = value)
比如两个函数
def task1(): time.sleep(2) print("in the task1") def task2(): time.sleep(2) print("in the task2") task1() task2()
要对这两个函数分别统计运行时间,但是要求统计之后输出:
the task1/task2 run time is : 2.00……
于是就要构造一个装饰器timer,并且需要告诉装饰器哪个是task1,哪个是task2,也就是要这样:
1 @timer(parameter=‘task1‘) # 2 def task1(): 3 time.sleep(2) 4 print("in the task1") 5 6 @timer(parameter=‘task2‘) # 7 def task2(): 8 time.sleep(2) 9 print("in the task2") 10 11 task1() 12 task2()
那么方法有了,但是我们需要考虑如何把这个parameter参数传递到装饰器中,我们以往的装饰器,都是传递函数名字进去,而这次,多了一个参数,要怎么做呢?
于是,就想到再加一层函数来接受参数,根据嵌套函数的概念,要想执行内函数,就要先执行外函数,才能调用到内函数,那么就有:
1 def timer(parameter): # 2 print("in the auth :", parameter) 3 4 def outer_deco(func): # 5 print("in the outer_wrapper:", parameter) 6 7 def deco(*args, **kwargs): 8 9 return deco 10 11 return outer_deco
首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:
1 timer = timer(parameter) 2 task1 = timer(task1)
完整的实现
1 import time 2 def timer(parameter): 3 def outer_wrapper(func): 4 def wrapper(*args, **kwargs): 5 if parameter == ‘task1‘: 6 start = time.time() 7 func(*args, **kwargs) 8 stop = time.time() 9 print("the task1 run time is :", stop - start) 10 elif parameter == ‘task2‘: 11 start = time.time() 12 func(*args, **kwargs) 13 stop = time.time() 14 print("the task2 run time is :", stop - start) 15 16 return wrapper 17 18 return outer_wrapper 19 @timer(parameter=‘task1‘)#1 timer = timer(parameter)此时timer=out_wrapper 2 task1 = timer(task1)此时相当于task1=wrapper(task1)
20 def task1():
21 time.sleep(2)
22 print("in the task1")
23 @timer(parameter=‘task2‘)
24 def task2():
25 time.sleep(2)
执行结果
in the task1 the task1 run time is : 2.000471591949463 in the task2 the task2 run time is : 2.000399589538574
原文:https://www.cnblogs.com/weststar/p/11360127.html