python装饰器
特征
器:指的是工具。
装饰:给被装饰对象添加额外的功能。
原则
开放封闭原则
开放:对扩展开放。
封闭:对修改封闭。
核心思想
在不改变被"装饰对象内部代码"和"原有调用方式"的基础之上添加额外功能。
在满足上述条件的情况下添加计算运行时间功能。

装饰器简易版本
装饰器的本来需求是要不改变原有的调用方式,因此可以使用将装饰器的函数名赋给和原来函数名相同的新变量名,来达成偷梁换柱的目的。
此时的index调用的其实是get_time2函数。

解决返回值问题
在解决了参数问题后,还有返回值的问题,当原函数有返回值时,上述的程序是无法返回的。因此还要添加返回值。
需要在装饰器内定义一个变量来接收原函数的返回值,然后在返回该变量。

认证装饰器
通过装饰器可以添加一个认证功能,利用全局变量,可以达成只用登录一次就使用全部功能d效果。
is_login = {'is_login': False}
def login(func):
def login1(*args, **kwargs):
if is_login.get('is_login'):
res = func(*args, **kwargs)
return res
username = input('请输入用户名:')
password = input('请输入密码:')
if username == 'tom' and password == '123':
is_login['is_login'] = True
res = func(*args, **kwargs)
return res
else:
print('用户名或密码错误')
return login1
def index():
print('登录成功')
def index1():
print('第二个功能')
index = login(index)
index1 = login(index1)
index()
index1()
效果为当第一次输入正确后,就不需要再登录,当第一登录失败后则需在此登录。

装饰器模板
通过上述的例子,可以推导出装饰器的一个万用模板。
利用该模板可以轻松完成装饰器。
def outer(func): def inner(*args, **kwargs): print('执行函数之前可以添加的额外功能') res = func(*args, **kwargs) # 执行被装饰的函数 print('执行函数之后可以添加的额外功能') return res # 将被装饰函数执行之后的返回值返回 return inner
装饰器语法糖
在完成装饰器后,可以使用装饰器语法糖(@函数名)来代替index = login(index)这句话,来使装饰器的调用更接近原函数。
语法糖的语法规范:紧贴在需要装饰的函数上方。
语法糖的内部原理:将紧贴的函数名作为参数传给装饰器函数调用。
import time def get_time1(func): def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) return res return get_time2 @get_time1 def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' print(index('tom'))

语法糖可以 多个叠加,当函数上方两个以上的语法糖时,他相当于是把下方的函数作为参数依次向上传,在运行时则是从上向下运行。
import time is_login = {'is_login': False} def get_time1(func): def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) return res return get_time2 def login1(func): def login2(*args, **kwargs): if is_login.get('is_login'): res = func(*args, **kwargs) return res username = input('请输入用户名:') password = input('请输入密码:') if username == 'tom' and password == '123': res = func(*args, **kwargs) return res else: print('用户名或密码错误') return login2 @login1 @get_time1 def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' index('tom')
因为要求登录的语法糖在上方,所以会先进行登录在进行计时。结果如下:

装饰器修复技术
我们现在使用了装饰器虽然看似调用的函数是原函数,但其实是其他的函数。为了使装饰器变得更像真的,可以使用装饰器修复技术。
他的作用是在使用装饰器后对函数进行输出,所看到的依旧是原函数的内存地址。

有参装饰器
在函数的模板中可以看到,双层的函数参数都是被固定的,有他们自己的功能。当我们还想往装饰器里传入参数时,就会遇到问题。
为解决这个问题,所采取的方法是在外层再包一层函数,将参数传入这层来供内层的装饰器使用。
此时需要的参数可以通过装饰器的语法糖来传入。
def more(another): from functools import wraps def get_time1(func): @wraps(func) def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) print('需要一个外层参数%s' % another) return res return get_time2 return get_time1 @more('示例') def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' index('tom')
运行结果如下:


浙公网安备 33010602011771号