一直有点小迷糊的装饰器,今天看了个视频,觉得讲的很细致,有种豁然开朗的感觉,于是决定梳理一遍记录下来,毕竟好记性不如烂笔头嘛~
一、装饰器的定义:通俗地说,就是装饰其他函数,为其他函数添加附加功能,本质上也是函数。
但要遵循两个原则:
1.不能修改被装饰函数的源代码
2.不能修改被装饰函数的调用方式
简言之,装饰器对被装饰函数是完全透明的。
二、在实现装饰器之前,我们还要先弄明白以下3个知识点:
1.函数即“变量”:在python中,无论是变量还是函数,在定义后,python都会在内存中为其分配一个地址,用来存放变量或函数的内容,在调用该变量或函数时,只要能在内存中找到变量或函数的内存地址,就可以正常运行。
举例来说,
def foo():
print('in the foo')
bar()
foo() # 如果在此刻调用函数foo,就会报错,因为函数bar还没有定义,内存中找不到其内容
def bar():
print('in the bar')
foo() # 而在此刻调用函数foo,就可以正常运行,因为调用前,函数bar已正常定义及分配内存地址
2.高阶函数
1) 把一个函数名做为实参传递给另一个函数
(实现在不修改被装饰函数源代码的情况下为其添加附加功能)
举例说明,
import time
def deco(func):
start_time = time.time()
func()
stop_time = time.time()
def test1():
time.sleep(3)
print('in the test1')
deco(test1) # 在不修改test1源代码的情况下,实现了计算函数test1的运行时间
2) 返回值中包含函数名
(实现了不修改被装饰函数的调用方式)
import time
def foo(func):
print(func) # 打印的是函数func的内存地址
return func
def bar():
time.sleep(3)
print('in the bar')
bar = foo(bar)
bar() # 显然,调用方式没有被改变
3.嵌套函数:在一个函数里再定义另一个函数
def gradpa():
x=1
def dad():
x=2
def son():
x=3
print(x)
son()
dad()
gradpa()
输出结果:3
注意:嵌套函数必须是在函数里再定义另一个函数,而高阶函数是调用另一个函数
三、装饰器的实现:高阶函数+嵌套函数
- 被装饰函数没有参数
import time
# 装饰器timer
def timer(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('the function run time is %s' %(stop_time - start_time))
return deco
@timer # 在python里,用@+装饰器函数名,放在被装饰函数上面,实际等同于test1 = timer(test1)
def test1():
time.sleep(3)
print('in the test1')
# test1 = timer(test1) # 结果为deco
test1() #等同于deco()
2.被装饰函数有0-n个参数
*args:不固定位置参数
**kwargs:关键字参数
import time
# 装饰器timer
def timer(func):
# 加入*args, **kwargs,无论被装饰函数拥有几个参数,都可以正常运行
def deco(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
stop_time = time.time()
print('the function run time is %s' %(stop_time - start_time))
return deco
@timer
def test2(name):
time.sleep(3)
print('in the test2',name)
# test2 = timer(test2) # 结果为deco
test2('Amy') #等同于deco('Amy')
3.被装饰函数有返回值
import time
# 装饰器timer
def timer(func):
def deco(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # 将函数func的返回值赋给res
stop_time = time.time()
print('the function run time is %s' %(stop_time - start_time))
return res # 返回res,就可以将被装饰函数的返回值输出
return deco
@timer
def test3(name):
time.sleep(3)
print('in the test3',name)
return 'hello test3'
# test3 = timer(test3) # 结果为deco
test3('Amy') #等同于deco('Amy')
4.根据条件给被装饰函数装饰不同功能
举例说明:假设有三个页面,index首页无需登录就能访问,home和bbs页面需要输入正确用户名密码才能进入,且home用本地认证,bbs用远程ldap验证
user = 'amy'
passwd = 'abc123'
# 用户登录认证装饰器
def auth(auth_type): # 第一层,传入不同的验证方式auth_type,返回函数loggin
def loggin(func): # 第二层,传入被装饰函数func,返回函数deco
def deco(*args, **kwargs):
if auth_type == 'local':
username = input('Username:').strip()
password = input('Password:').strip()
if username == user and password == passwd:
res = func(*args, **kwargs)
return res
else:
exit('Invalid username or password!')
elif auth_type == 'ldap':
print('sorry, i don\'t konw ldap')
return deco
return loggin
def index():
print('welcome to index page')
@auth(auth_type='local') # 执行结果等同于home = deco()
def home():
print('welcome to home page')
@auth(auth_type='ldap')
def bbs():
print('welcome to bbs page')
index()
home()
bbs()