一.装饰器的定义
在实际的开发过程中,对代码的修改是封闭的,对扩展是开放的。换句话说,提交的代码上线之后是无法修改的!
有时需要在不修改源代码的情况下修改已经存在的函数,给函数增加新的功能 。
以下面例子为例来理解为何要使用装饰器:
def f1():
print('This is a function...')
f1()
假如函数要添加新功能:输出当前的系统时间,直接修改代码可以实现。但是实际中是无法修改原本函数中代码的。
import time
def f1():
# print(time.time())
print('This is a function...')
f1()
不修改原本函数的代码,通过新的函数也可以实现该功能的添加。
import time
def f1():
print('This is a function...')
def f2():
print('This is a function...')
def print_current_time(func):
print(time.time())
func()
print_current_time(f1)
print_current_time(f2)
问题来了,这样是可以实现功能,也没有改变函数的源代码。但是调用函数的方式也改变了。假如有很多个函数都需要添加新的功能,改动就不方便!所以引入了装饰器这个概念。
装饰器实际上是一个函数。它把一个函数作为输入并且返回另外一个函数。
import time
def decorator(func):
def wrapper():
print(time.time())
func()
return wrapper
@decorator
def f1(): #函数的源代码没有修改
print('This is a function...')
def f2():
print('This is a function...')
f1() #原本的函数调用方式也没有改变
f2()
二.装饰器的使用
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先实现新增功能,再紧接着调用原始函数。
import time
def decorator(func):
def warpper(*args,**kwargs):
print(time.time())
func(*args,**kwargs)
return warpper
@decorator
def f1(func_name):
print('This is a function ' + func_name)
@decorator
def f2(func_name1,func_name2):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
@decorator
def f3(func_name1,func_name2,**kwargs):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
print(kwargs)
f1('test')
f2('test1','test2')
f3('test1','test2',a=1,b=2,c='westos')
练习1:装饰器实现一个函数计时器
"""
1.问题1:被装饰的函数有返回值
2.问题2:如何保留被装饰函数的函数名和帮助信息文档
"""
import random
import string
import time
li = [random.choice(string.ascii_letters) for i in range(100)]
def RunTime(func):
"""这是一个装饰器Runtime"""
def wrapper(*args,**kwargs):
"""这是一个wrapper函数"""
start_time = time.time()
res = func(*args,**kwargs)
end_time = time.time()
run_time = end_time - start_time
print('运行时间为: %.5f' %run_time)
return res
return wrapper
@RunTime
def join_add():
print(','.join(li))
join_add()
@RunTime
def fun_list(n):
return [i * 2 for i in range(n)]
c=fun_list(10)
print(c)
print(fun_list.__doc__)
print(fun_list.__name__)
装饰器的正确使用:
import functools
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('[%s] 函数名: %s, 运行时间: %6f,运行返回值结果: %d' %(time.ctime(),fun.__name__,end_time - start_time,res))
return res
return wrapper
@add_log
def add(x,y):
time.sleep(1)
return x + y
add(1,2)
练习2:
import functools
import inspect
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
#inspect.getcallargs返回一个字典,key值是形参,value值
#是对应的实参{'name':'root'}
inspect_res = inspect.getcallargs(fun,*args,*kwargs)
print('inspect的返回值: %s' %inspect_res)
if inspect_res.get('name') == 'root':
res = fun(*args,**kwargs)
return res
else:
print('not root user!')
return wrapper
@is_admin
def add_user(name):
print('添加用户信息...')
def del_user(name):
print('删除用户信息...')
add_user('root')
练习3:
"""
编写装饰器required_types, 条件如下:
# 1). 当装饰器为@required_types(int,float)确保函数接收到的
每一个参数都是int或者float类型;
# 2). 当装饰器为@required_types(list)确保函数接收到的每一>个参数都是list类型;
# 3). 当装饰器为@required_types(str,int)确保函数接收到的每
一个参数都是str或者int类型;
# 4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类
型
"""
import functools
def required_types(*kinds):
def required_int(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
for i in args:
if not isinstance(i, kinds):
# print('TypeError:参数必须为',kinds)
# break
raise TypeError('参数必须为%s,%s' % kinds)
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required_int
@required_types(float, float)
def add(a, b):
return a + b
print(add(1.1, 2.0))
三.多个装饰器
一个函数可以有多个装饰器
def decorator_a(fun):
print('Get in decorator_a')
def inner_a(*args, **kwargs):
print('Get in inner_a')
res = fun(*args, **kwargs)
return res
return inner_a
def decorator_b(fun):
print('Get in decorator_b')
def inner_b(*args, **kwargs):
print('Get in inner_b')
res = fun(*args, **kwargs)
return res
return inner_b
@decorator_a
@decorator_b
def f(x):
print('Get in f')
return x * 2
f(2)