python语言中的装饰器应该算是这门语言中的一个难点了,作为一个初学者,好像也知道是怎么回事,可是否真正理解了,那还得存疑。最近偶然看到老男孩的Egon老师的视频,我才觉得真正明白装饰器是怎么一回事了。Egon老师那种抽丝剥茧式的讲解,真是让人耳目一新。虽然有些时候略显繁琐,但对于一个困难知识点来说,再没有这种方式更好的讲解了。接下来,我就着刚看过视频的热乎劲儿,把我对装饰器的理解记录下来,以防止哪一天忘记了。
- 什么是装饰器
- 装饰器一种特殊对象,可以给其它对象添加功能,而不改变所装饰对象的使用方式。比如说:如果一个函数被一个装饰器装饰,那么从用户的角度来看,应该感觉不到有任何变化,函数的调用方式,参数以及返回值都不发生任何变化。
- 装饰器用到了一个Python中的一个重要概念:闭包。什么是闭包?条件有两个:一是一定要有一个内部函数。二是这个内部函数一定要访问包含这个内部函数的外层函数的变量。
- 还利用了Python中的函数的特性:可以作为参数传递,也可以作为返回值被返回。 - 如何来写一个装饰器(以函数装饰器为例)
- 我们先来考虑,如何给一个函数增加功能,却不改变它的调用方式。
# sol3 - 1: 通过在内外层函数中使用两次(*args, **kwargs),可以把外层函数的参数原封不动地传递到内层。
# 从而就可以把内层函数的参数写活,但是变更了调用方式,要通过wrapper来调用index
def wrapper(*args, **kwargs):
start = time.time()
index(*args, **kwargs)
stop = time.time()
print(stop - start)
wrapper('xiaolee', 'zd')
# sol 3 -2: 要想不改变函数的调用方式,只能通过在wrapper函数外再包上一层函数。这个方案还不能接收内层函数的返回值。
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print(stop - start)
return wrapper
index = timer(index)
index('ggg', 'dddd')
# sol3 - 3:通过res变量接收内层函数的返回值,并作为wrapper函数的返回值返回。
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
index = timer(index)
print(index('zz', 'yy'))
# sol 4: 语法糖
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
@timer
def index(x, y):
time.sleep(1)
print('heloo %s, your company %s' % (x, y))
index('xxxx', 'ccccc')
- 带参数的装饰器
# 带参装饰器:实际上,一层闭包就可以将一个参数向内传递一层。
# 以加简单的认证功能为例
def authentication():
user = input('Please input your username: ').strip()
password = input('Please input your password: ').strip()
if user == 'zylee' and password == 'wlsoft':
print('authentication successful!!!')
return True
else:
print('authentication failed!!!')
return False
def auth(db_style):
def deco(func):
def wrapper(*args, **kwargs):
# auth code
if db_style == 'mysql':
if authentication():
res = func(*args, **kwargs)
return res
elif db_style == 'file' and authentication():
if authentication():
res = func(*args, **kwargs)
return res
else:
print("error!!!!")
return wrapper
return deco
# @auth('mysql')这一行的功能:先执行auth('mysql'),将deco作为返回值返回。实际上@作用在了deco上。
# @的作用有二:一是将所装饰的函数的地址作为参数传给deco。二是deco(index)的返回值赋给index。运行之后,相当于执行wrapper函数。
@auth('mysql')
def index(x, y):
print('hello, %s and %s' % (x, y))
index('a', 'b')