一、什么是闭包
闭包(closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。以上两种解释都太抽象了。还有一种比较容易的理解方式:当一个函数内部嵌套另一个函数,内部函数体用到了外部函数的局部变量,并且外部函数的返回结果是内部函数名(或者说是内部函数对象),这样在执行外部函数时得到的是内部函数对象,此时虽然外部函数已经结束,但接下来继续执行内部函数时,内部函数仍能访问到所引用的外部函数的那个局部变量,也就是说此时外部函数虽然执行完毕,但内存并没有释放,还需等待内部函数执行完毕后才释放内存。这种现象就叫做闭包。
defouter(name):definner(age):print("name:", name, "age:", age) #内部函数inner调用了外部函数outer的变量name
return inner #结果返回的时inner对象
f = outer("lilei") #执行outer函数得到的是inner对象,相当于f = inner
f(18) #相当于执行inner(18)
结果:
name: lilei age:18
我们可以通过__closure__属性来查看函数是否存在闭包,如果存在,则返回一个cell对象,并且通过cell对象可以查出闭包中所包含的所有外部变量。如果函数不存在闭包,则返回None.
defouter(name):definner(age):print("name:", name, "age:", age) #内部函数inner调用了外部函数outer的变量name
return inner #结果返回的时inner对象
f = outer("lilei") #执行outer函数得到的是inner对象,相当于f = inner
f(18) #相当于执行inner(18)
print(f.__closure__)print(f.__closure__[0].cell_contents)
结果:
name: lilei age:18(,)
lileidefouter(name,age):print("name:", name, "age:", age)
outer("lilei",18)print(outer.__closure__)
结果:
name: lilei age:18None
二、闭包的应用——装饰器
python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。装饰器函数的外部函数传入我们要装饰的函数名字,返回经过装时候函数的名字,内层函数(闭包)负责修饰被修饰的函数。
1、实质:是一个函数;
2、参数:是你要装饰的函数名(并非函数调用);
3、返回:是装饰完的函数名(也非函数调用);
4、作用:为已经存在的对象添加额外的功能;
5、特点:不需要对对象做任何的代码上的改动。
说了这么多,装饰器到底有什么作用呢?python装饰器就是用于拓展原来的函数功能的一种函数,这个函数的特殊之处在于台的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数代码的前提下给函数增加新的功能。一般而言我们想要拓展原来的函数功能,最直接的办法就是侵入代码里面修改,但这种方法有弊端,因为修改源代码不能保证源代码的其他模块功能的有效性。并且修改源代码还违反了软件开发中的一个原则”开放-封闭“原则,简单来说,它规定已经实现功能的代码不应该被修改,但可以被扩展。即:封闭,已经实现的功能代码块不应该被修改 开放,对现有功能的扩展开放。
举例:比如你是一家视频网站的后端开发工程师,你们网站的python专区有以下三种学习板块:python初级视频课程,python中级视频课程和python高级视频课程,现在要求对中级视频课程和高级视频课程只有vip才能观看。这种要求如何实现呢?
1、写法一
account ={"is_authenticated": False, #用户登陆成功就把它改成True
"username": "lilei", #数据库中的用户信息
"pass_word": "123" #数据库中的用户密码
}deflogin():if account["is_authenticated"] ==False:
user_name= input("user:").strip()
password= input("password:").strip()if user_name == account["username"] and password == account["pass_word"]:print("welcome login....")
account["is_authenticated"] =Trueelse:print("wrong username or password")if account["is_authenticated"] ==True:print("已通过验证...")defhome():print("python专区".center(30, "-"))deffirst_stage():print("python初级阶段学习视频".center(30, "-"))defsecond_stage():
login()print("python中级阶段学习视频".center(30, "-"))defthird_stage():
login()print("python高级阶段学习视频".center(30, "-"))
home()
first_stage()
second_stage()
third_stage()
View Code
这种写法直接对源代码进行了修改,违反了“开放-封闭”原则。
2、写法二
account ={"is_authenticated": False, #用户登陆成功就把它改成True
"username": "lilei", #数据库中的用户信息
"pass_word": "123" #数据库中的用户密码
}deflogin(func):if account["is_authenticated"] ==False:
user_name= input("user:").strip()
password= input("password:").strip()if user_name == account["username"] and password == account["pass_word"]:print("welcome login....")
account["is_authenticated"] =Trueelse:print("wrong username or password")if account["is_authenticated"] ==True:
func()defhome():print("python专区".center(30, "-"))deffirst_stage():print("python初级阶段学习视频".center(30, "-"))defsecond_stage():print("python中级阶段学习视频".center(30, "-"))defthird_stage():print("python高级阶段学习视频".center(30, "-"))
home()
first_stage()
login(second_stage)
login(third_stage)
View Code
这种写法,虽然没有改变源代码,但改变了函数的调用方式。
3、写法三
account ={"is_authenticated": False, #用户登陆成功就把它改成True
"username": "lilei", #数据库中的用户信息
"pass_word": "123" #数据库中的用户密码
}deflogin(func):def inner(): #在内部再定义一层函数
if account["is_authenticated"] ==False:
user_name= input("user:").strip()
password= input("password:").strip()if user_name == account["username"] and password == account["pass_word"]:print("welcome login....")
account["is_authenticated"] =Trueelse:print("wrong username or password")if account["is_authenticated"] ==True:
func()return inner #返回内部函数对象
defhome():print("python专区".center(30, "-"))deffirst_stage():print("python初级阶段学习视频".center(30, "-"))defsecond_stage():print("python中级阶段学习视频".center(30, "-"))defthird_stage():print("python高级阶段学习视频".center(30, "-"))
home()
first_stage()
second_stage= login(second_stage) #相当于inner对象
third_stage = login(third_stage) #相当于inner对象
second_stage() #执行inner()
third_stage() #执行inner()
View Code
这种写法就实现了既不改变源代码也不改变调用方式,但实现了功能的拓展,但是无法给second_stage() 或 third_stage()函数传参数。
4、写法四
account ={"is_authenticated": False, #用户登陆成功就把它改成True
"username": "lilei", #数据库中的用户信息
"pass_word": "123" #数据库中的用户密码
}deflogin(func):def inner(*args, **kwargs): #在内部再定义一层函数
if account["is_authenticated"] ==False:
user_name= input("user:").strip()
password= input("password:").strip()if user_name == account["username"] and password == account["pass_word"]:print("welcome login....")
account["is_authenticated"] =Trueelse:print("wrong username or password")if account["is_authenticated"] ==True:
func(*args, **kwargs)return inner #返回内部函数对象
defhome():print("python专区".center(30, "-"))deffirst_stage():print("python初级阶段学习视频".center(30, "-"))defsecond_stage():print("python中级阶段学习视频".center(30, "-"))defthird_stage(vip_level):if vip_level > 3:print("python高级阶段学习视频".center(30, "-"))else:print("vip level is too lower")
home()
first_stage()
second_stage= login(second_stage) #相当于inner对象
third_stage = login(third_stage) #相当于inner对象
second_stage() #执行inner()
third_stage(1) #执行inner(1)
View Code
这是装饰器的终极写法,既可以不改变源码实现对原有功能的扩展,还可以给函数传递参数。