一、闭包函数
闭包函数=函数嵌套定义+函数对象+名称空间与作用域
闭包函数:在函数中(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。
1、闭:指的是该函数是定义在一个函数内部的函数
2、包:值得是该函数访问了一个来自于外层函数的变量
为函数体传参的方法:
'''方案一:直接使用参数的形式传递'''
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
'''方案二:把函数体想要的参数包给它(即使用闭包的概念)'''
def outter(x):
def wrapper(): # wrapper = 闭包函数的内存地址
print(x)
return wrapper # 一定不要加括号
f1 = outter(111) # f = 闭包函数的内存地址
f2 = outter(222) # f = 闭包函数的内存地址
f1()
f2()
-----------
111
222
乍一看会感觉使用闭包来传参数非常的麻烦,我们之前使用函数,需要参数都是直接传给他,方便也快捷,但是在某些场景下我们定死了某个函数无法直接传参数,那就必须通过其他方式传参数,即闭包的方式,下面介绍的装饰器就是闭包的使用。
二、@符号的使用
@符号作用:
(1) 可以自动把@符号下面的函数当成参数传递给装饰器
(2) 把新函数进行返回,让新函数去替换旧函数,以实现功能的扩展.
如:
@timer # 等于func1 = timer(func1),其目的是为了让使用者不改变调用方式,它依然在调用func1,但是实际已经被我们换成了timer
def func1():
print(1)
三、叠加多个装饰器的运行步骤
结论:(记住结论既可)
加载顺序:自下而上
执行顺序:自上而下运行内层的wrapper函数
验证过程:
# 叠加多个装饰器
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('wrapper1====>')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('wrapper2====>')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址
def wrapper3(*args,**kwargs):
print('wrapper3====>')
res3=func3(*args,**kwargs)
return res3
return wrapper3
# index=wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址
@deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址
def index(x,y):
print('index=>',x,y)
index(1,2)
-----------------------
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
四、无参装饰器
什么是装饰器:
器:工具
装饰:为被装饰者添加额外的功能
为何要有装饰器:
软件一旦上线运行之后,就应该遵循开放封闭原则:
1、开放指的是对拓展新功能开放
2、封闭指的是对修改源代码封闭
这种情况下,我们在写新功能时,若需要用到新的参数,就无法直接通过原函数来传了,必须通过其他方式来传参,目前我们想到的方法,是两种传参的另一种的方式,闭包函数
定义装饰器的目的:
定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的
ps:
不修改被装饰对象指的是定义与调用都不能修改
所以下述行为都违反了开放封闭原则:
①、修改被装饰对象定义时的源代码
②、修改被装饰对象的调用方式
如何用装饰器:
'''无参装饰器基本格式'''
def wrapper(func):
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return func
return inner
'''
切记不能在此处加括号,加括号就代表运行,相当于先把inner运行一遍,然后拿到返回值return出去.
return 出去的值是无法加括号在运行的。
'''
# 例子
def wrapper(func):
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return res
return inner() # 切记不能在此处加括号,加括号就代表运行
@wrapper
def func1():
print(11)
func1()
-----------------
11
TypeError: 'NoneType' object is not callable
'''
前面打印出来的11其实是运行wrapper里的inner产生的,所以以上程序的运行流程是
1.先把func1传给wrapper,然后运行了wrapper中的代码,返回 inner()
2.inner() 就开始执行inner函数,即运行了一遍func1,所以打印了11
3.此时的func1 = inner() 而inner()又等于func1(),func1没有返回值,默认返回None
4.变成了None(),报错
'''
无参装饰器的应用场景以及构建装饰器步骤:
需求:有一个index函数,此时我们要在index函数基础上添加一个计算运算时间的功能,因为是公司项目,我们不能修改原函数的调用方式,也不能修改源代码。
# 一、被装饰器对象index如下
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
# 二、为index添加计算运算时间与登录功能
import time
from functools import wraps
def timmer(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(end-start)
return res
return wrapper
def login(func):
def wrapper(*args,**kwargs):
name = 'yang'
pwd = '123'
inp_name = input("请输入您的用户名:").strip()
inp_pwd = input("请输入您的密码").strip()
if inp_name == name and inp_pwd == pwd:
print("登录成功")
res = func(*args,**kwargs)
return res
return wrapper
@timmer
@login
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
五、有参装饰器
对于不再需要新参数的装饰器,两层就可以解决了,但是当我们的装饰器需要新的参数,如在登录的时候,我们要判断我们用户名与密码的来源,此时需要外部将来源传进来。而两层的装饰器中,第一层,是为了给原函数传参数,他的参数不能动,而第二层,因为装饰器语法@的原因,也已经定死了此处只能传一个函数名,也不能传参数,所以我们需要构造第三层来接受外部的参数,而在内部的函数可以在不改动自己依然可以获取到外层的函数.
'''有参装饰器模版'''
def outter2(x,y,z,a,b):
def outter1(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter1
例子:
def outter(engine = 'file'):
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if engine == "file":
print('基于file的认证')
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
elif engine == 'mysql':
print('基于mysql的认证')
elif engine == 'ldap':
print('基于ldap的认证')
else:
print('未知的engine')
return wrapper2
return deco2
@outter(engine='mysql') # @deco2 # index=deco2(index)
def index(x,y):
print('index=>',x,y)
index(1,2) # index=>wrapper
六、类装饰器
我们也可以用类来装饰函数
# 利用类静态方法装饰
class MyClass:
@staticmethod
def zhuangshi1(func):
def newfunc():
print("装饰前")
func()
print("装饰后")
return newfunc
def __call__(self, *args, **kwargs):
return self.zhuangshi2(*args, **kwargs)
def zhuangshi2(self,func):
def newfunc():
print('装饰前')
func()
print('装饰后')
return newfunc
# 利用类静态方法,本质和原来的用函数装饰一样
@MyClass.zhuangshi1
def func1():
print('运行func1')
func1()
# 利用__call__方法
@MyClass()
def func2():
print('运行了func2')
func2()