装饰器
储备知识1:*args和**kwargs
def index(x,y):
print(x,y)
def wrapper(*args,**kwargs):
index(*args,**kwargs)
wrapper(1,y=2)
# 会将wrapper的参数原封不动的传给index()
储备知识2:名称空间与作用域
# 名称空间的“嵌套”关系实在函数定义阶段即检测语法的时候确定的
储备知识3:函数对象
# 可以把函数当作参数传输
# 可以把函数当作返回值返回
def index():
pass
def foo(function):
return function
foo(index)
# 传给foo的是index的内存地址,传给foo的是一个函数
储备知识4:函数的嵌套定义
def outter(function):
def wrapper():
pass
return wrapper
储备知识 5:闭包函数
# 闭函数:指该函数是内嵌函数
# 包函数:该函数对外层函数的作用域名字的引用(不是对全局作用域)
def outter():
x=111
def wrapper():
print(x)
return wrapper
func = outter()
func()
# func虽然可以全局调用wrapper,但外部包裹了outter()
1、什么是装饰器
- 器指的是一个工具,大多数场景都是定义成函数
- 装饰指的是为其他事物添加额外的东西点缀
- 总结:装饰器指的是定义一个函数或者类,用来为其他函数或类添加额外的功能
2、为何要用装饰器
- 开放封闭原则
- 开放:指的是对拓展功能是开放的
- 封闭:指的是对修改源代码是封闭的
- 即在不修改源码及调用方式的前提下拓展功能
3、装饰器案例
无参装饰器实例
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
from functools import wraps # 伪装原函数的所有属性
"""
需求:
1、原函数为index
2、不修改原函数源码和调用的方式
3、增加统计函数运行时间功能
4、新增的装饰器同样可以装饰其他函数
"""
def timmer(func):
@wraps(func) # 伪装原函数的所有属性
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print('运行时间:{}秒'.format(int(stop - start)))
# 伪装原函数的返回值
return res
# 伪装原函数函数名
# wrapper.__name__ = func.__name__
# 伪装原函数文档信息
# wrapper.__doc__ = func.__doc__
return wrapper
@timmer # 语法糖 index = timmer(index)
def index(x, y):
"""
被装饰函数
"""
time.sleep(2)
print(x, y)
@timmer # 语法糖 other = timmer(other)
def other(name):
"""
其他被装饰函数
"""
time.sleep(3)
print('{} from other'.format(name))
index(200, 300)
print('函数名:{}\n函数文档信息:{}'.format(index.__name__, index.__doc__))
other('message')
print('函数名:{}\n函数文档信息:{}'.format(other.__name__, other.__doc__))
有参装饰器实例
为什么需要有参装饰器:
由于语法糖@的限制,装饰器只能由一个参数,并且该参数是用来接收被装饰对象的内存地址。
当wrapper还需要额外的参数时,此时就需要有参装饰器进行装饰。这里的有参指的是给wrapper的参数而不是原函数。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
需求:
为index和other添加认证功能
index的数据来源为文件
other的数据来源为数据库
"""
from functools import wraps
import pymysql
def auth(dt_type):
# 为装饰器传参数
def deco(function):
@wraps(function)
def wrapper(*args, **kwargs):
usn = input('your username >>> ').strip()
pwd = input('your password >>> ').strip()
if dt_type == 'file':
print('login from file')
with open('data/user', 'rt', encoding='utf-8') as f:
for line in f:
username, passwords = line.strip().split()
if usn == username and pwd == passwords:
res = function(*args, **kwargs)
return res
else:
print('username or password error!')
elif dt_type == 'mysql':
print('login from mysql')
db = pymysql.connect(user='root',password='1234',host='127.0.0.1',
database='user',port=3306,charset='utf-8')
cursor = db.cursor()
data = cursor.fetchall()
for line in data:
username, passwords = line.strip().split()
if usn == username and pwd == passwords:
res = function(*args, **kwargs)
return res
else:
print('username or password error!')
else:
print('path error!')
return wrapper
return deco
@auth(dt_type='file')
def index(x, y):
"""
被装饰函数
"""
print('from index --> ', x, y)
@auth(dt_type='mysql')
def other(x, y):
"""
被装饰函数
"""
print('from other --> ', x, y)
index(1, 2)
other(3, 4)
可以看出,所谓有参装饰器是在原装饰器上再进行一次闭包后通过语法糖对原装饰器进行传参。
同时使用多个装饰器时,装饰器的加载顺序为自下而上,而运行顺序为自上而下
@deco1
@deco2
@deco3(x=1)
def index():
pass
# 加载顺序
# @deco3(x=1) ---> @deco2 ---> @deco1
# 运行顺序
# @deco1 ---> @deco2 ---> @deco3(x=1)