一、闭包函数
1、什么叫闭包函数
所谓的闭包函数就是定义在函数内部的函数,且只引用外部函数作用域的变量。
def outter(x, y): def my_max(): if x > y: return x return y return my_max res1 = outter(1,40) # res就是my_max函数的内存地址 print(res1()) # 40
2、闭包函数的作用
在没学闭包函数之前,给函数体传值的时候只能定义形参接收外部参数的传参,函数体中的变量名接收值。闭包函数也可以用来给函数体传参,通过定义在外部函数作用域内的形参或变量传参。
二、装饰器
1、什么是装饰器
装饰器的本质是一个闭包函数。
装饰器是一个工具,给被装饰的对象添加新功能,并且不改变被装饰的函数和其调用方法。
2、装饰器的特点
装饰器遵循开放封闭原则:所谓开放是指对扩展开放,可以对装饰器不断添加新的功能;封闭是指对修改封闭,外部调用装饰器的时候不能修改内部的结构和变量。
在设计装饰器的时候应遵守在不改变被装饰对象的源代码和调用方式的前提下设计装饰器。
3、装饰器的设计
装饰器一般分为两层装饰器和三层装饰器、
两层装饰器在设计的时候只是用来 装饰函数的,不能给内部变量传参
三层装饰器在两层装饰的基础上再外层定义了一个函数,用来接收外部的传参,解决了内部不能接收外部参数的问题。
在使用两层装饰器的时候,外层函数的定义一个形参用来接收被装饰的函数的函数名,内层函数定义的形参用*和**,方便接收所有的参数,在内层函数中调用外层函数的形参名,在其后加上括号,括号内写上参数*和**
# 两层装饰器模板 def outter(func): def inner(*args,**kwargs): print('执行被装饰函数之前 你可以做的操作') res = func(*args,**kwargs) print('执行被装饰函数之后 你可以做的操作') return res return inner
三层装饰器的内部两层与两层装饰器相同,只是在最外层的函数的形参中定义内部需要使用的参数。
# 三层装饰器函数模板 def outter2(*args, **kwargs): def outter1(func): def inner(*args, **kwargs): print("被装饰函数之前执行的功能") res = func(*args, **kwargs) print("被装饰函数之后执行的功能") return inner return outter1
两层装饰器的使用:
将被装饰的函数名当作参数传给装饰器函数,用被装饰的函数名接收该函数调用的返回值,在调用被装饰的函数。
import time def index(): time.sleep(3) print('澳门最大线上赌场开业啦 性感tank在线发牌!') def outter(func): # func = 最原始的index函数的内存地址 def get_time(): start = time.time() func() # func = index函数的内存地址() 直接调用 end = time.time() print('index run time:%s'%(end-start)) return get_time index = outter(index) # outter(最原始的index函数内存地址) # index指向get_time函数的内存地址 index() """ 运行结果如下: 澳门最大线上赌场开业啦 性感tank在线发牌! index run time:3.0000386238098145 """
先调用装饰器传入装饰器内部需要的变量,用一个变量名接收,再用该变量名加括号传入被装饰的函数的函数名,用被装饰的函数的函数名接收,最后再调用被装饰的函数名。
user_dic = {'is_login':None} def login_auth2(data_source): def login_auth(func): def inner(*args,**kwargs): if user_dic['is_login']: res = func(*args, **kwargs) return res else: if data_source == 'file': username = input('please input your username>>>:').strip() password = input('please input your password>>>:').strip() if username == 'jason' and password == '123': user_dic['is_login'] = True res = func(*args,**kwargs) return res else: print('username or password error') elif data_source == 'MySQL': print('from MySQL') elif data_source == 'ldap': print('ldap') else: print('暂无该数据来源') return inner return login_auth
通过以上的使用发现,调用装饰器太过麻烦,于是就有了语法糖的用法,语法糖的出现大大的便于了装饰器的调用。
语法糖的使用方式是@+装饰器函数的函数名,放在需要被装饰的函数前一行。
5、关于多个装饰器装饰同一个函数
建议采用语法糖的方法使用装饰器。
多个装饰器在装饰同一个函数的时候注意:装饰的时候,顺序是从下往上,即对被装饰的函数添加新功能的顺序是从下往上;在执行的时候是从上往下,即在程序执行的时候,从上往下一个一个开始执行。
def fun1(fun): def inner(*args,**kwargs): print("111") fun() print("111") return inner def fun2(fun): def inner(*args,**kwargs): print("222") fun() print("222") return inner def fun3(fun): def inner(*args,**kwargs): print("333") fun() print("333") return inner @fun1 @fun2 @fun3 def index(): print("你好!") index() """ 运行结果: 111 222 333 你好! 333 222 111 """
6、被装饰函数的修复技术
由于被装饰后的装饰调用函数名的时候对应的不是原来函数的内存地址,不便于对被装饰函数的调用以及查看引入了被装饰函数修复技术,实质是在设计装饰函数时调用warps装饰器。
from functools import wraps def outter(func): @wraps(func) # 装饰器修复技术 def inner(*args,**kwargs): print('执行被装饰函数之前 你可以执行的操作') res = func(*args,**kwargs) print('执行被装饰函数之后 你可以执行的操作') return res return inner