python 之闭包函数
闭包函数=名称空间与作用域 + 函数嵌套 + 函数对象
一、引入
大前提
-
闭包函数是对名称空间与作用域, 函数对象, 函数嵌套的综合运用
-
闭包函数 = 名称空间与作用域 + 函数嵌套 + 函数对象
-
核心点:变量的查找关系是以函数定义阶段为准的
二、什么是闭包函数?闭 & 包
-
闭函数:指的是定义在函数内部的函数
-
包函数:闭函数引用了一个来自于外层函数的变量
-
特点:访问的变量不是自己的,而是外层函数的
-
-
以上函数,合到一起为闭包函数,即:闭包函数=闭函数+包函数
'总结一句话来说' : 定义在函数内部的函数, 并且该函数包含对外部函数作用域中名字的引用,该函数就称为闭包函数
代码一:
def f1():
x = 10
def f2():
print(x)
# 这个f2就是闭包函数。
代码二:
def outter():
name='egon'
def inner():
print('my name is %s' %name)
return inner #注意不能加括号,加了括号就是返回的不是inner这个函数,而是inner函数调用完之后返回的是一个值,inner函数默认没有值,会返回一个None。
f=outter() #其实就是f=inner
print(f) #<function outter.<locals>.inner at 0x0000014E06AE3F70>
f() #my name is egon
-
注意:作用域在函数定义阶段就规定死了,与调用的位置无关
定义阶段已经规定死了到'outter'下面的"x"去拿 def outter(): x=2 def inner(): # x=1 print('from inner',x) return inner 在全局里面定义也无效 f=outter() # f=inner print(f) x=11111111111111111 f() # 2 把它装进一个函数,在函数里面再定义也是无效的 def foo(): x=111111111111111111 f() foo() # 2
三、为函数传值的方式
目前为止,我们得到了两种为函数体传值的方式,一种是直接将值以参数的形式传入,另外一种就是将值包给函数
方式一: 将值以参数的形式传入
代码一:
def f2(x):
print(x)
f2(1)
f2(2)
f2(3)
代码二:将以下"url"以参数的形式传入(也是最常用)
import requests #导入requests模块
def get(url):
response=requests.get(url)下载
if response.status_code == 200:
print(response.text)
get('https://www.baidu.com')
get('https://www.python.org')
# 提示提示:requests模块是用来模拟浏览器向网站发送请求并将页面内容下载到本地,需要事先安装:pip3 install requests
方式二: 以包的形式传入(使用内嵌函数把函数包裹, 为被包裹的函数提供作用域名字的引用)
代码一:
def outter(x):
def wrapper():
print(x) #按照查找的优先级去查找"x"
return wrapper #吐出了函数"wrapper"的内存地址
f = outter(10) #这里返回的值就是函数"wrapper"的内存地址,赋值给"f",加括号就调用
f() #10
代码二:
def f1(x):
# x = 333
def f2():
print(x)
return f2
f = f1(333) # 把原本属于f1局部的函数的名字, 拿到了全局.
print(f) # <function f1.<locals>.f2 at 0x000001DBF8C3CE50>
f() # 333, 实现了多次调用同一种传参的功能, 不需要重复传参.
f() # 333
f() # 333
代码三:这是个获取网络源码的功能
import requests
def outter(url):
def get():
response=requests.get(url)
if response.status_code == 200:
print(response.text)
return get
baidu = outter('https://www.baidu.com')
baidu()
对比两种方式,方式一在下载同一页面时需要重复传入url
,而方式二只需要传一次值,就会得到一个包含指定url
的闭包函数,以后调用该闭包函数无需再传url
# 方式一下载同一页面
get('https://www.python.org')
get('https://www.python.org')
get('https://www.python.org')
……
# 方式二下载同一页面
python=page('https://www.python.org')
python()
python()
python()
……
闭包函数的这种特性有时又称为惰性计算。使用将值包给函数的方式,在接下来的装饰器中也将大有用处
四、闭包的作用:
被引用的非全局变量也称作自由变量,这个自由变量会与内层函数产生一个绑定关系。 自由变量不会在内存中消失。
1、保证数据安全。函数外无法访问自由变量。
def func1():
li = []
def func2(x):
li.append(x)
return li
ruturn func2
f = func1()
print(f(100)) # [100]
print(f(100)) # [100, 100]
print(f(100)) # [100, 100, 100]
#li就是自由变量,并没有消失,但无法从函数外访问。
2、为内部函数传参。
返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
def f1(x):
def f2():
print(x,y) # 优先使用外层的作用域,不会被全局所影响。
x = 10
y = 20
return f2
x = 1
y = 2
f1(x)()
10 20
五、如何判断一个嵌套函数是不是闭包:
print(func.__code__.co_freevars)
# 只要返回值有自由变量那么就是闭包
def func():
x = 10
def f1():
print(x)
return f1
print(func().__code__.co_freevars)
('x',)