发布时间:2018-02-26 20:38:49编辑:admin阅读(3736)
本文介绍几个装饰器案例,来分析装饰器是如何调用的
获取函数运行时间的例子
写装饰器,不可以一步到位,要慢慢一点一点的来
先写好2个函数
import time def test1(): time.sleep(1) print('in the test1') def test2(): time.sleep(2) print('in the test2') test1() test2()
执行输出
in the test1
in the test2
在不修改源代码的情况下,新增一个功能呢?
这个时候,需要用到 高阶函数
import time def deco(func): start_time = time.time() func() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) def test1(): time.sleep(1) print('in the test1') def test2(): time.sleep(2) print('in the test2') deco(test1) deco(test2)
执行输出
in the test1
the func run time is 1.0009195804595947
in the test2
the func run time is 2.0000431537628174
注意:
执行的时候,不能写deco(test1()),为什么呢?这样写,是把test1函数执行的结果,传给deco了。
deco函数,不需要执行结果,它需要一个函数即可。
Pycharm编辑器写代码的时候,有自动补全功能,切记这里,要把括号删掉才行。
上面实现了显示执行时间的功能,下面考虑一个问题,如何在此基础上,直接执行
test1()和test2() 就可以实现上面的功能
需要用到变量的引用
def deco(func): start_time = time.time() func() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) def test1(): time.sleep(1) print('in the test1') def test2(): time.sleep(2) print('in the test2') test1 = deco(test1) test2 = deco(test2) test1() test2()
执行报错
TypeError: 'NoneType' object is not callable
因为deco没有return,无法得到函数内存地址
再改一下
import time def deco(func): start_time = time.time() #返回函数内存地址 return func stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) def test1(): time.sleep(1) print('in the test1') def test2(): time.sleep(2) print('in the test2') test1 = deco(test1) test2 = deco(test2) test1() test2()
执行输出
in the test1
in the test2
从结果可以看出,函数的调用方式没有变,执行结果也没有变。搞了半天,啥都没干。
因为deco中直接returun了,所以打印时间没有输出。
上面的应用都是高阶函数,还缺嵌套函数,就成了
下面写一个嵌套函数。
def timer(): def deco(): pass
能不能把嵌套函数的形式融入到deco函数中呢?
把deco函数的代码直接拷贝进来,最后return deco
一个函数只有一个return,把中间的return修改为func()
将func参数移动到函数最上层
def timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) return deco
完整代码如下:
import time def timer(func): #time(test1) func=test1 def deco(): start_time = time.time() func() #run test1() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) return deco def test1(): time.sleep(1) print('in the test1') def test2(): time.sleep(2) print('in the test2') print(timer(test1))
执行输出
function timer.
结果返回deco函数的内存地址
有了函数的内存地址,把它赋值给一个变量,执行变量就可以了
print(timer(test1))改成
test1 = timer(test1) test1()
执行输出
in the test1
the func run time is 1.0003840923309326
这里,效果就实现了,源代码和执行方式都没有改变。
注意: 最后的test1()行函数,已经不是原来的函数了,而是被装饰过的函数。
执行函数,需要2个步骤,太麻烦了,这不是最终效果
python 提供一个语法,用来执行装饰器函数,语法
@函数名 被装饰的函数名
这一句,需要加在被装饰函数的上一行
我删除了test2(),最终完整代码如下:
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time def timer(func): #timer(test) func=test1 def deco(): start_time = time.time() func() #run test1() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) return deco @timer #test1=timer(test1) def test1(): time.sleep(1) print('in the test1') test1()
执行输出
in the test1
the func run time is 1.0006184577941895
test1()函数上面的@timer
就相当于
test1 = timer(test1)
注意:上面写的装饰器,还不够完美
举个例子,再加一个函数,去装饰它
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time def timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) return deco @timer def test1(): time.sleep(1) print('in the test1') @timer def test2(name): print("test",name) test1() test2("zhang")
执行报错
TypeError: test2() missing 1 required positional argument: 'name'
为什么呢?
test2上面的@timer就相当于
test2 = timer(test2)
timer(test2) -> 调用deco() -> 调用func() ->调用原test2(name)
注意,调用原test2函数的时候,需要传一个参数才行,而func()调用它的时候,没法传参数,所以程序报错。
为了解决传参的问题,需要调整一下deco函数,要求能接受参数。由于被装饰的函数,千奇百怪,有参数,没参数的都存在。使用*args,**kwargs就可以表示任意的参数。
最后终极代码如下:
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time def timer(func): def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) return deco @timer def test1(): time.sleep(1) print('in the test1') @timer def test2(name): time.sleep(2) print("test",name) test1() test2("zhang")
执行输出
in the test1
the func run time is 1.000878095626831
test zhang
the func run time is 2.0003161430358887
网页登陆
代码如下:
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time user,passwd = 'zhang','abc123' def auth(func): def wrapper(*args,**kwargs): username = input("username:").strip() password = input("password:").strip() if user == username and passwd == password: print("\033[32;1mUser has passed authentication\033[0m") func(*args,**kwargs) else: exit("\033[31;1mInvalid username or password\033[0m") return wrapper def index(): print("welcome to index page") @auth def home(): print("welcome to home page") @auth def bbs(): print("welcome to bbs page") index() home() bbs()
执行输出
输出了2次用户名和密码
为什么呢?因为home和bbs页面,需要登录才能访问。
下面加一个验证方式
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time user,passwd = 'zhang','abc123' def auth(auth_type): print("auth func:",auth_type) def outer_wrapper(func): def wrapper(*args, **kwargs): print("wrapper func args:",*args, **kwargs) username = input("username:").strip() password = input("password:").strip() if user == username and passwd == password: print("\033[32;1mUser has passed authentication\033[0m") res = func(*args, **kwargs) print("---after authentication") return res else: exit("\033[31;1mInvalid username or password\033[0m") return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") def home(): print("welcome to home page") @auth(auth_type="ldap") def bbs(): print("welcome to bbs page") index() home() bbs()
执行输出
现在还没有判断是哪种方式
完整代码如下:
#!/usr/bin/env python # coding: utf-8 __author__ = 'www.py3study.com' import time user,passwd = 'zhang','abc123' def auth(auth_type): #print("auth func:",auth_type) def outer_wrapper(func): def wrapper(*args, **kwargs): #print("wrapper func args:",*args, **kwargs) if auth_type == "local": username = input("username:").strip() password = input("password:").strip() if user == username and passwd == password: print("\033[32;1mUser has passed authentication\033[0m") res = func(*args, **kwargs) print("---after authentication") return res else: exit("\033[31;1mInvalid username or password\033[0m") elif auth_type == "ldap": print("This is LDAP,I can't do it") pass return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") def home(): print("from home page") @auth(auth_type="ldap") def bbs(): print("from bbs page") index() home() bbs()
执行输出
执行bbs,采用的是ldap方式,我就直接输出了一段话,判断逻辑不会写啊!
上一篇: python 装饰器
下一篇: python 生成器
47745
46237
37110
34627
29229
25886
24745
19863
19417
17909
5716°
6315°
5836°
5888°
6985°
5829°
5846°
6361°
6316°
7673°