本文介绍几个装饰器案例,来分析装饰器是如何调用的
获取函数运行时间的例子
写装饰器,不可以一步到位,要慢慢一点一点的来
先写好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方式,我就直接输出了一段话,判断逻辑不会写啊!