装饰器
一、概念
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。比如我们要给每一个函数新增一个打印
logger
日志,我们就可以使用装饰器,在不修改原函数的前提下,新增功能又能做到代码的解耦
二、装饰器的几个原则及学习装饰器必要的知识储备
1、装饰器的几个原则
- 1.不能修改被装饰的函数
- 2.不能修改被装饰的函数的调用方式
2、学习装饰器必要的知识
- 1.函数的理解
- 2.高阶函数
- 3.函数的嵌套
三、装饰的原理
1、定义一个装饰器(计算函数执行时间)
import time def timer(func): def wrapper(): start_time = time.time() res = func() end_time = time.time() print('程序运行时间:{0}'.format(end_time - start_time)) return res return wrapper
2、定义一个要执行的函数
def foo(): time.sleep(3) print('主函数')
3、根据上面几个原则,我们来调用函数
foo
if __name__ == "__main__": foo = timer(foo) foo()
4、解答上面的代码
- 1.定义的装饰器是一个高阶函数,被装饰的函数作为参数传递进去
- 2.在满足装饰的原则下,我们把高阶函数执行后重新赋值给
foo
函数 - 3.
foo()
函数的执行就没改变原有函数的调用方式
5、使用装饰器
@
上面第四点中2和3步骤在
python
中直接使用@
语法糖来处理@timer def bar(): time.sleep(2) print('主函数') if __name__ == "__main__": bar()
四、被装饰的函数带参数
不太懂函数的参数、可变参数、关键字参数的可以回顾下函数的认识这一章节
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('执行时间:{0}'.format(end_time - start_time))
return res
return wrapper
@timer
def foo(name, gender):
time.sleep(3)
print(name)
print(gender)
if __name__ == "__main__":
foo('张三', gender='男')
五、模拟用户登录的装饰器
如果你使用过
django
就会使用到一个用户登录拦截的装饰器
def auth_login(func):
def wrapper(*args, **kwargs):
username = input('用户名:').strip()
passwd = input('密码:').strip()
if username == 'hello' and passwd == 'word':
res = func(*args, **kwargs)
return res
else:
print('你输入的用户名或者密码错误')
return wrapper
@auth_login
def foo():
print('主页')
if __name__ == "__main__":
foo()
六、装饰器传递参数
- 我们学习函数的时候就说过foo()带了()就表示函数执行,不带()仅仅是返回一个函数的内存地址
- 如果装饰器要传递参数进去,我们自然想的是多嵌套一层函数
# 定义一个用户登录的装饰,默认是手机号码登录(也有可能是email,username方式登录)
def auth(type='mobile'):
def auth_func(func):
def wrapper(*args, **kwargs):
if type == 'mobile':
print('你是用手机号码登录')
print(args, kwargs)
res = func(*args, **kwargs)
return res
elif type == 'email':
print('email方式登录')
res = func(*args, **kwargs)
return res
elif type == 'username':
print('username方式登录')
res = func(*args, **kwargs)
return res
return wrapper
return auth_func
@auth('mobile')
def test(mobile, passwd):
print('主页')
@auth('email')
def test1():
print('主页1')
if __name__ == "__main__":
test('110', '123')
test1()
七、使用类来创建一个装饰器
创建一个数据库操的
log
的装饰器
1、具体实现代码
from functools import wraps from datetime import datetime # 创建一个类的装饰器 class Log(object): def __init__(self, logfile='log.log'): self.logfile = logfile def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): self.writelog(*args, **kwargs) return func(*args, **kwargs) return wrapper # 把日志写到本地 def writelog(self, *args, **kwargs): time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_str = time + ' 操作人:{0[0]} 进行了【{0[1]}】操作'.format(args) # 写入本地文件中 with open(self.logfile, 'a', encoding='utf8') as file: file.write(log_str + '\n') @Log() def printLog(name, type): print('姓名:{0},type:{1}'.format(name, type)) if __name__ == "__main__": printLog('张三', '查询') printLog('李四', '新增')
2、执行结果(本地文件夹下多一个文件)
2018-06-24 10:47:40 操作人:张三 进行了【查询】操作 2018-06-24 10:47:40 操作人:李四 进行了【新增】操作