一、高阶函数的返回值为函数--初识装饰器
通过上一节的学习我们了解了,当我们定义一个函数的def时,它的return值一般为一个数值或是一个运算表达式,但是在我们的学习过程中会发现一些问题,当我们已经写好的一段代码并且逻辑非常绕,但是现在需要我们对他做一些改动时你会发现完全无从下手,只要改变之前的代码就会报错,并且是接踵而至麻烦不断。所以我们这里就要引进一个更方便更快捷---------装饰器
1、什么是装饰器
所谓装饰器就是把函数包装一下,为函数添加一些附加功能,装饰器就是一个函数,参数为被包装的函数,返回包装后的函数
2、装饰器的作用
可以极大简化代码,避免每个函数编写重复性的代码
(1)、打印日志:@log
(2)、检测性能:@performance
(3)、数据库事务:@transaction
(4)、URL路由:@post('/register')
3、装饰器应用场景
如果你想在执行函数之前做什么或者执行函数之后做什么, 建议使用装饰器;
例:假设六一儿童节到了,你的产品经理需要你去给所有的ATM机上打上“祝小朋友们节日快乐!”
由于ATM机上接口众多,程序更多,你无法每一个函数对他进行改动并且银行并不会允许你这么干,那么就需要一个既不改变原函数而且还能达到经理的需求------装饰器,一个可以在函数上“胡作非为”的函数!
def add_info(fun): # fun = saveMoney def wrapper(): print("欢迎使用ICBC银行ATM") print("祝小朋友们节日快乐!") fun() # saveMoney() return wrapper # python的语法糖 @add_info # saveMoney= addinfo(saveMoney) def saveMoney(): print("存钱中.......") @add_info def transferMoney(): print("转账中......") @add_info def getMoney(): print("取钱中.......") saveMoney() transferMoney() getMoney()
上面的程序中我们模拟了在银行存取钱的过程,我们没有在源程序上做任何改动,但是我们给每个程序都加上了python的语法糖(让这段代码看起来更加甜美——完美);
所以当我们在实际工作当中,我们有时需要去看某段函数或者程序的运行时间或是日至时,我们的装饰器就能起到很好的作用;
二、简易装饰器模板
import functools def add_info(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): # 函数执行之前做的操作 res = fun(*args, **kwargs) # 函数执行之前做的操作 return res return wrapper
这里的模板并不能满足所有的装饰器,但可以完成简单的装饰器操作;
装饰器就是为了达到:
封闭:已实现的功能代码块
开放:对扩展开放
三、functools.wraps
上面的装饰器已经有了最基本的功能实现,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的信息没有被赋值到装饰器的内部。装饰器一般返回一个包装器(wrapper),而functools.wraps就是装饰包装器的装饰器。例如:函数的注释信息四、初级-时间装饰器
def timeit(fun): # fun = add def wrapper(x,y): start_time = time.time() fun(x,y) # hello() end_time = time.time() print("%s函数运行时间为%s" % (fun.__name__, end_time - start_time)) return wrapper
时间装饰器用来测试代码运行时间,下面我们可以测试一些代码的运行时间;
我们代运行上面的主函数时发现,一旦实参和形参对不上系统就会报错,那么我们的装饰器也就还需要改进,我们要达到对各种函数都能够完美的运行;
五、终级-时间装饰器
import time import functools # 需求: 编写一装饰器timeit, 用来装饰某函数执行时间的装饰器; def timeit(fun): # fun = world # 2. 注意: functools.wraps(fun): 可以保留add, world等函数的函数名, 帮助文档等属性信息; @functools.wraps(fun) def wrapper(*args, **kwargs): # kwargs = {'a':1, 'b':2} """ this is wrapper function。。。。 :param args: :param kwargs: :return: """ start_time = time.time() temp = fun(*args, **kwargs) # world(a=1, b=2) end_time = time.time() print("%s函数运行时间为%s" % (fun.__name__, end_time - start_time)) return temp return wrapper
@timeit # add = timeit(add) # add = wrapper def add(x,y): """ add(x:int, y:int)->int: :param x: :param y: :return: """ return x+y # hello() # wrapper() print(add(1,3)) print(add.__name__) print(add.__doc__)
终极时间装饰器,实现了我们的目标,可以对不同的数据类型进行操作;
.__name__ :查看函数名称
.__doc__ :查看函数的解析,我们对函数各个变量的定义及其他们的用法;
例:我们使用其他的数据类型来测试
@timeit # world = timeit(world) # world = wrapper def world(**kwargs): """this is **kwargs test""" return kwargs print(world(a=1, b=2)) print(world.__name__) print(world.__doc__)
我们能看到和上面一样的,运行时间等其的函数信息;
六、return的理解
def fun(): print('hello') return "world" # 在执行函数时, 遇到return, 函数就执行结束;return后面的永不执行; print('gcc') print(fun())
在执行函数时, 遇到return, 函数就执行结束;return后面的永不执行,有点像break;
七、初级-登陆验证装饰器
还记得我们前面写过一个学生登陆系统,在这里如果我们要加入管理员管理系统我们也可以借助装饰器来实现;因为管理员的权限更大,我们需要在某些操作之前进行身份验证,例如:删除学生信息、增添新的学生信息等;
下面我们列出一个简单的登陆验证装饰器例子:
import functools def is_admin(fun): # fun=add_student @functools.wraps(fun) def wrapper(*args, **kwargs): # kwargs = {'name':'root'} if kwargs.get('name') == 'root': temp = fun(*args, **kwargs) return temp else: print("not root/admin user, no permisson add student") return wrapper
这段函数,首先这是一个闭包,外层函数的返回值为一个函数这是一个装饰器,在运行fun()函数之前我们先进行判断,如果name=root时我们运行fun函数,否则我们将弹出对话框,表示你没有权限去做;
我们模拟一个学生信息系统:
@is_admin # add_student = is_admin(add_student) def add_student(name): print("添加学生信息.....") add_student(name='root')
当我们输入正确时就会运行我们的主函数,否则:
上面的两个例子就为就为我们提供了很好的解释了关于装饰器的实际作用,
八、终极-初级-登陆验证装饰器
inspect模块
上面的输入显得不太像我们日常生活中的输入方式,没人会在输入用户名前还要敲上“用户名:"三个字所以为了更形象,这里我们要引入一个新的模块,import inspect 这个模块的inspect.getcallargs返回一个字典, key值为:形参, value值为对应的实参;
终极版-登陆验证装饰器
import functools import inspect def is_admin(fun): # fun=add_student @functools.wraps(fun) def wrapper(*args, **kwargs): # kwargs = {'name':'root'} # inspect.getcallargs返回一个字典, key值为:形参, value值为对应的实参; inspect_res = inspect.getcallargs(fun, *args, **kwargs) print("inspect的返回值: %s" %(inspect_res)) if inspect_res.get('name') == 'root': temp = fun(*args, **kwargs) return temp else: print("not root/admin user, no permisson add student") # 抛出异常 return wrapper @is_admin # add_student = is_admin(add_student) def add_student(name): # print("添加学生信息.....") add_student('root')
上面的装饰器就更加性形象的为我们展示了在学生登陆前的身份验证;相信这在以后的工作学习中都会有很重要的作用。