python 带参装饰器_python带参数的装饰器

装饰器更高级的用法是可以带参数。

带参数的装饰器

先来看一个不带参数的装饰器

1

2

3

4

5

6

7

8

9

10

11

12

13

14import time

def timeit(fn):

def wrap(*args,**kwargs):

start = time.time()

ret = fn(*args,**kwargs)

print(time.time() - start)

return ret

return wrap

@timeit

def sleep(x):

time.sleep(x)

1sleep(3)

3.0034420490264893

这里打印出来的是执行sleep函数所消耗的自然时间,但在执行此函数时所消耗的cpu时间真的有3.0034420490264893秒吗?当然不是。利用time包中的time.clock方法可以计算得到代码执行所消耗cpu的时间,那怎样来修改上边的timeit函数,让其即能计算代码执行的自然时间,也能计算代码执行所消耗cpu的时间?做如下改进:

1

2

3

4

5

6

7

8

9

10

11

12

13def timeit_1(process_time=False):

cacl = time.clock if process_time else time.time

def timeit_2(fn):

def wrap(*args,**kwargs):

start = cacl()

ret = fn(*args,**kwargs)

print(cacl() - start)

return ret

return wrap

return timeit_2

def sleep_1(x):

time.sleep(x)

1timeit_1(True)(sleep_1)(3)

0.020000000000000018

1timeit_1(False)(sleep_1)(3)

3.0038363933563232

1timeit_1()(sleep_1)(3) # 参数process_time是一个默认参数,所以可以不传递值,默认为False

3.003509283065796

上边的调用过程是怎样的呢?分解一下,如下:

1fn1 = timeit_1(True)

上边调用timeit_1(True),函数return回了timeit_2,并把fn1这个变量指向了调用结果,即指向了timeit_2,这里的timeit_2也是一个函数,此函数接收一个参数

1fn2 = fn1(sleep_1)

这里调用fn1(sleep_1),其实就是调用了timeit_2(sleep_1),并把fn2这个变量指向了调用后的结果,即指向了warp,这里的warp也是一个函数,此函数能接收任意的参数

1fn2(3)

0.009999999999999787

上边调用fn2(3),其实是调用了wrap(3),即执行了wrap函数内的语句,此函数内的ret = fn(*args,**kwargs)语句中的fn其实是指向了sleep,所以在执行wrap函数时,sleep_1函数才真正被执行。

既然装饰器可以用魔法来装饰一个函数,那上边经过改进过的装饰器是不是也能装饰一个函数呢?如下:

1

2

3@timeit_1(False)

def sleep_2(x):

time.sleep(x)

1sleep_2(3)

3.0039477348327637

如果想计算代码执行的cpu时间,那如下即可:

1

2

3@timeit_1(True)

def sleep_3(x):

time.sleep(x)

1sleep_3(3)

0.0

这个魔法又发生了什么呢?

其实质就是在没有用魔法的情况下直接timeit_1(True)(sleep_3)(3)。而当使用@这个魔法后,当代码执行到此行时,解析器会执行timeit_1(True),timeit_1实质就是一函数,接收一个参数,并返回一个timeit_2函数。当代码执行到@所在语句时,会把所装饰的sleep_3函数作为一个参数传递给timeit_1(True)的调用结果,即timeit_2这个函数,即sleep_3这个函数已作为一个变量传递给了timeit_2(fn)中的fn参数,并返回了一个wrap函数,在接下的调用sleep_3(3)这个操作,其实此时的sleep_3这个函数已不是原先的def sleep_3(x):中的sleep_3函数,而是一个指向了wrap的函数,wrap函数接收任何参数,所以把当执行sleep_3(3)时,把参数3传递给了wrap函数,并执行内部的代码,内部代码中ret = fn(*args,**kwargs)中的fn函数依赖还是指向原先的sleep_3(x)这个函数。

这里也有一个简单的记忆方式,如果一个函数被装饰器所装饰,在调用这个函数时其实不再是调用表面上看上去的这个函数,以

1

2

3@timeit_1(True)

def sleep_3(x):

time.sleep(x)

来做说明。当执行到有@魔法所在行时,相当于执行了sleep_3 = timeit_1(True)(sleep_3),即指向了wrap函数,既然sleep_3指向了wrap函数,那我们执行sleep_3(3)时,其实就是在进行wrap(3)这样的函数调用,记住,函数名也是一个变量。

再来举一个带参数的装饰器的例子,比如有一个函数,只有在对有许可权限的用户开放,执行此函数的用户没有在认证列表里的,就不会执行这个函数。这个该如何实现呢?如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14def check(allow_users):

def inner_check(fn):

def wrap(username,*args,**kwargs):

'''This is wrap'''

if username in allow_users:

return fn(username,*args,**kwargs)

return "You are illegal users"

return wrap

return inner_check

@check(['zhaochj','zcj'])

def private(username):

'''The authentication'''

return "You are legitimate users"

1private('zhaochj')

'You are legitimate users'

1private('tom')

'You are illegal users'

这样就可以对调用函数进行身份验证。

python中一个函数有一些属性是函数本身具有的,比如__name__属性是查看函数名称,__doc__是查看函数文档的等等。如果一个函数被装饰器装饰过后,这个函数的这些属性会发生怎样的变化呢?以上边的check装饰器和private函数为例子,如下:

1private.__name__

'wrap'

1private.__doc__

'This is wrap'

private函数的名称是wrap了,文档也是wrap函数的文档,这是怎么回事?上边已经说过,这里的private函数被装饰器装饰后它已不再是原来的private函数,private这个函数名称会被指向到wrap这个函数对象,那当然用上边的private.__name__和private.__doc__查看函数的属性就会是wrap函数的属性。那怎样来修正呢?可以这样做,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17def check_1(allow_users):

def inner_check(fn):

def wrap(username,*args,**kwargs):

'''This is wrap'''

if username in allow_users:

return fn(username,*args,**kwargs)

return "You are illegal users"

wrap.__name__ = fn.__name__

wrap.__doc__ = fn.__doc__

return wrap

return inner_check

@check_1(['zhaochj','zcj'])

def private_1(username):

'''The authentication'''

return "You are legitimate users"

1private_1.__name__

'private_1'

1private_1.__doc__

'The authentication'

通过在装饰器把__name__和__doc__重新赋值后就能更正这个问题,但对一个函数来说像__name__这样类似的属性有许多,如果都是这样手工来修正显然是不现实的,所以python提供了一个wraps装饰器来自动修正这个问题,wraps在functools这个包中,所以可以这样来修正这个问题,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18import functools

def check_1(allow_users):

def inner_check(fn):

@functools.wraps(fn)

def wrap(username,*args,**kwargs):

'''This is wrap'''

if username in allow_users:

return fn(username,*args,**kwargs)

return "You are illegal users"

return wrap

return inner_check

@check_1(['zhaochj','zcj'])

def private_1(username):

'''The authentication'''

return "You are legitimate users"

1private_1.__name__

'private_1'

1private_1.__doc__

'The authentication'

@functools.wraps(fn)这个装饰器相当于执行了wrap.__name__ = fn.__name__这样的操作。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值