1.装饰器工作原理:装饰器本身是一个函数,它参数是一个函数, 2.在装饰器内定义一个新函数,而新的函数又调用传进来的函数(定义一个嵌套函数,调用原函数) 3.最后返回这个新的函数(嵌套函数) 4.通过语法糖,将原函数名指向返回新的函数,实现未修改原码,从而添加功能 例一:原理如下
def func3(): print('Have a nice day!') # 定义装饰器 def outer(a): def inner(): a() print('~~~~~~~~~~~~') return inner # func3 = outer(func3) func3()
例二:python中正确的书写语法,以及可以添加判断语句 # 定义装饰器 def outer(fun): def new_la(age): if age < 0: age = 0 fun(age) return new_la # 装饰器在装饰时,需要在每个函数前面加上,显然有些麻烦,Python提供了一种语法糖,即通过以下方式等通于例一中的 @outer def la(age): print('age is %d' % age) la(-10)
例三
""" 装饰器实现一个函数计时器 """ import time import random import string import functools li = [random.choice(string.ascii_letters) for i in range(100)] # 问题1:被装饰的函数有返回值 # 问题2:如何保留被装饰函数的函数名和帮助信息文档 def timeit(fun): """这是一个装饰器timeit""" # @functools.wraps(fun) #保留原函数注释和函数名 def wrapper(*args, **kwargs): # 接收可变参数和关键字参数 """这是一个wrapper函数""" # args:元组 kwargs:字典 # 在函数执行之前 start_time = time.time() # 执行函数 res = fun(*args, **kwargs) # 在函数执行之后 end_time = time.time() print('运行时间为:%.6f' % (end_time - start_time)) return res return wrapper @timeit def fun_list(n): """这是fun_list函数,被timeit装饰""" return [2 * i for i in range(n)] @timeit def fun_map(n): """这是fun_map函数,被timeit装饰""" return list(map(lambda x: x * 2, range(n))) print(fun_list(50000)) print(fun_map(50000)) #通过timeit修饰器我们可以打印出原函数运行时间,发现内置函数+匿名函数实现功能的运行时间比我们通过循环所需时间要少 def fun(): """这是fun函数""" print('hello') print(fun_list.__doc__) print(fun_list.__name__) # 本来函数名称与注释,应该是经过修饰器返回的新函数,但是我们在修饰器中加入@functools.wraps(fun)内置修饰器保留原来的名称与注释
# 添加前的结果
# 添加后的结果
例四
""" # 创建装饰器, 要求如下: # 1. 创建add_log装饰器, 被装饰的函数打印日志信息; # 2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx """ import functools import time 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) # 睡眠1s return x + y add(1, 10)
""" 编写一个required_ints,条件如下: 1.确保函数接收的每一个参数都是整形; 2.如果参数不是正int,打印提示报错 3.给装饰器添加参数 """ import functools def required_ints(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): opt = True for i in args: if not isinstance(i, int): # 内置函数用户判断变量i类型是否为int print('ErrorType:', i, '参数必须是整形') opt = False else: if opt == True: return fun(*args, **kwargs) return wrapper @required_ints def lala(*args): print(args)
lala(1,2.2,3) lala(1, 2, 3)
例五
#带参数的修饰器,在前面例四的基础上加入如下代码
import functools def required_types(*types): # 在原来修饰器外嵌套一个新的函数用来接受参数 def required_ints(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): opt = True for i in args: if not isinstance(i, types): # 修改int为types我们就可以对比多个类型 print('ErrorType:', i, '参数必须是整形') opt = False else: if opt == True: return fun(*args, **kwargs) return wrapper return required_ints # @required_ints @required_types(int, float) # 新的修饰器加入参数 def lala(*args): print(args) lala(1, 2, 3, 2.2)
#当调用添加float值是,就不会报错