python函数修饰器_python函数装饰器详解

python装饰器(fuctional decorators)简单来说就是修改其他函数的函数。

这样的函数需要满足两个个条件:

1、不能修改原函数的源代码

2、不能改变原函数的调用方式

需要达到的效果:增加函数的功能

假设,我们已经定义了一个函数

import time

def test():

time.sleep(2)

print('The is test')

现在需要为这个程序添加一个功能:显示该程序运行的时间

一、预备知识

理解装饰器的难点在于:

1、理解python一切皆为对象的含义

2、高阶函数

3、嵌套函数

1、一切皆对象

这里只是简单看一下,理解函数即变量即可。后面学习深入会有更深入的理解。

1 >>> defsay(name):2 ... return 'hello'+name3 ...4 >>> print(say())5 >>> print(say('Michael'))6 hello Michael7 >>>

8 #我们可以将函数名作为变量赋值给另一个变量,换句话说变量可以指向函数

9 >>> greet=say10 >>> print(greet())11 >>> print(greet('Bob'))12 hello Bob13 #变量删除,对象也就不存在了

14 >>> delsay15 >>> say('Jack')16 Traceback (most recent call last):17 File "", line 1, in

18 NameError: name 'say' is notdefined19 >>>

2、高阶函数

高阶函数有两类:

a.把一个函数名当作实参传给另外一个函数

b.返回值中包含函数名

python一切皆为对象,实际上函数名就是函数的地址,也可以认为,是函数的一个标签。这样就可以把函数名作为一个参数传给另一个函数,在另一个函数里做一些操作,就可以在不修改源代码的基础上,为函数增加一些功能。

1 importtime2 deftest():3 print('This is test')4

5 defdeco(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 istest

the func run time is 0.0

那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名

1 deftest():2 time.sleep(3)3 print('This is test')4

5 defdeco(func):6 print('This is deco')7 returnfunc8

9 test =deco(test)10 test()

这种方式虽然可以不改变调用方式,但是无法完成计时功能,运算结果

This isdeco

Thisis test

3、嵌套函数

1 importtime2 deftimer(func):3 defdeco():4 start =time.time()5 func()6 stop =time.time()7 print('The func running time is %s' % (stop -start))8 returndeco9

10 deftest():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 istest

The func running timeis 2.000332832336426

那么通俗一点的理解就是:

把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)。

二、真正的装饰器

根据以上分析,装饰器在装饰时,需要在每个函数前面加上:

test = timer(test)

显然有些麻烦,Python提供了一种语法糖,即:

@timer

这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。

以上为无参形式。

1 importtime2

3 defdecorator(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 returnwapper10

11 @decorator #test1=decorator(test1) =wapper大盒子替换到中盒子

12 deftest():#表面是小盒子,实际上是中盒子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 time2

3 deftimer(func)4 def deco(*args, **kwargs):5 start =time.time()6 func(*args, **kwargs)7 stop =time.time()8 print(stop-start)9 returndeco10

11 @timer12 def test(parameter):

13 time.sleep(2)14 print("test is running!")15 test()

带返回值的装饰器

test()返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:

deftimer(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 time2

3 deftimer(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 returnres #函数带有返回值10 returndeco11

12 @timer13 def test(parameter):

14 time.sleep(2)15 print("test is running!")16 return "Returned value"

17 test()

更复杂的装饰器

又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:

@decorator(parameter = value)

比如两个函数

deftask1():

time.sleep(2)print("in the task1")deftask2():

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 deftask1():3 time.sleep(2)4 print("in the task1")5

6 @timer(parameter='task2') #7 deftask2():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 returndeco10

11 return outer_deco

首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:

1 timer =timer(parameter)2 task1 = timer(task1)

完整的实现

1 importtime2 deftimer(parameter):3 defouter_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 returnwrapper17

18 returnouter_wrapper19 @timer(parameter='task1')#1 timer = timer(parameter)此时timer=out_wrapper 2 task1 = timer(task1)此时相当于task1=wrapper(task1)

20 deftask1():21 time.sleep(2)22 print("in the task1")23 @timer(parameter='task2')24 deftask2():25 time.sleep(2)

执行结果

inthe task1

the task1 run timeis : 2.000471591949463

inthe task2

the task2 run timeis : 2.000399589538574

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值