名称空间、作用域、闭包
名称空间namespace
存放名字的地方,是对栈区的划分,有了名称空间之后,就可以在栈区中存放相同的名字,详细的,名称空间分为三种
内置名称空间
存放的名字:存放的是python解释器内置的名字
举例说明
In [2]: input
Out[2]: <function input(prompt=None, /)>
In [3]: print
Out[3]: <function print>
存活周期: python解释器启动则产生,python解释器关闭则销毁(python程序结束后)
全局名称空间
存放的名字: 只要不是函数内定义,也不是内置的,剩下的都是全局名称空间的名字。
存活周期: python文件执行则产生,python文件运行完毕后销毁
下面的模块名,非函数内部的变量名都是全局名称空间
import os
x = 10
if 13 > 3:
y = 20
if 3 == 3:
z = 30
# func=函数的内部地址
def func():
a = 111
b = 222
class Foo:
pass
局部名称空间
存放的名字: 在调用函数时,运行函数体代码过程中产生的函数内的名字。
存活周期: 在调用时存活,函数调用完毕则销毁,同一个函数调用多变,调用几次,产生几次名称空间。
def func(a, b):
pass
# 产生4个名称空间
func(10, 1)
func(11, 12)
func(13, 14)
func(15, 16)
案例
COUNTRY = "中国"
CITY_LIST = ["北京", "上海", "深圳"]
def download():
url = "http://www.xxx.com"
CITY_LIST = ["河北", "河南", "山西"]
print(url)
print(COUNTRY)
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
print(COUNTRY)
print(CITY_LIST)
download()
upload()
COUNTRY = "中华人民共和共国"
CITY_LIST = [11, 22, 33]
download()
upload()
中国
['北京', '上海', '深圳']
http://www.xxx.com
中国
['河北', '河南', '山西']
中国
['北京', '上海', '深圳']
http://www.xxx.com
中华人民共和共国
['河北', '河南', '山西']
中华人民共和共国
[11, 22, 33]
名称空间的加载顺序
1内置名称空间 > 2全局名称空间 > 3局部名称空间
名称空间销毁顺序
1局部名称空间 > 2全局名称空间 > 3内置名称空间
名字查找的优先级
当前所在的位置(三种名称空间)向上一层一层查找
内置名称空间
全局名称空间
局部名称空间
如果当前在局部名称空间: 局部名称空间 -> 全局名称空间 -> 内置名称空间
input = 333
def func():
input = 444
print(input)
func()
>>> 444
如果当前在全局名称空间:全局名称空间 -> 内置名称空间
input = 333
def func():
# input = 444
print(input)
func()
>>> 333
# input = 333
def func():
# input = 444
print(input)
func()
>>> <built-in function input>
input = 333
def func():
input = 444
func()
print(input)
>>> 333
挖个坑,注意
def func():
print(x)
x = 111
func()
>>> 111
def func():
print(x)
func()
x = 111
# 结果报错
名称空间的“嵌套”关系
是以函数定义阶段为准,与调用位置无关
x = 1
def func():
print(x)
def foo():
x = 222
func()
foo()
>>> 1
函数嵌套定义
input = 111
def f1():
input = 222
def f2():
input = 333
print(input)
f2()
f1()
>>> 333
input = 111
def f1():
input = 222
def f2():
# input = 333
print(input)
f2()
f1()
>>> 222
input = 111
def f1():
# input = 222
def f2():
# input = 333
print(input)
f2()
f1()
>>> 111
# input = 111
def f1():
# input = 222
def f2():
# input = 333
print(input)
f2()
f1()
>>> <built-in function input>
示例 结果会报错
x = 111
def func():
print(x)
x = 222
func()
# 结果报错
作用域 --> 作用范围
全局作用域:内置名称空间、全局名称空间
-
全局存活
-
全局有效:被所有函数共享
x = 111
def foo():
print(x, id(x))
def bar():
print(x, id(x))
foo()
bar()
print(x, id(x))
# 结果如下
111 2671133742896
111 2671133742896
111 2671133742896
局部作用域:局部名称空间的名字
-
临时存活
-
局部有效:函数内有效
LEGB (了解)
LEGB含义解释:
L-Local(function);函数内的名字空间
E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G-Global(module);函数定义所在模块(文件)的名字空间
B-Builtin(Python);Python内置模块的名字空间
# 最外层是Builtin
# global
def f1():
# Enclosing
def f2():
# Enclosing
def f3():
# local
pass
global和nonlocal
global
如果在局部想要修改全局的名字对应的值(不可变类型),需要用global
x = 111
def func():
x = 222
func()
print(x)
>>> 111
x = 111
def func():
# 声明x是全局的名字,不需要再造新值了
global x
x = 222
func()
print(x)
>>> 222
可变类型,通常情况下不需要用global
l = [111, 222]
def func():
l.append(333)
func()
print(l)
>>> [111, 222, 333]
案例
COUNTRY = "中国"
CITY_LIST = ["北京", "上海", "深圳"]
def download():
url = "http://www.xxx.com"
global CITY_LIST
CITY_LIST = ["河北", "河南", "山西"]
print(CITY_LIST)
global COUNTRY
COUNTRY = "中华人民共和国"
print(COUNTRY)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
['河北', '河南', '山西']
中华人民共和国
中华人民共和国
['河北', '河南', '山西']
nonlocal(了解)
修改函数外层函数包含的名字对应的值(不可变类型)
从当前函数的外一层开始找
需求:修改f1内的x
x = 0
def f1():
x = 111
def f2():
global x
x = 222
f2()
print('f1内的x:', x)
f1()
print(x)
# 结果如下
f1内的x: 111
222
x = 0
def f1():
x = 111
def f2():
nonlocal x
x = 222
f2()
print('f1内的x:', x)
f1()
print(x)
# 结果如下
f1内的x: 222
0
针对可变类型,一样不需要nonlocal
def f1():
x = []
def f2():
x.append(111)
f2()
print('f1内的x:', x)
f1()
>>> f1内的x: [111]
嵌套注意
名称空间的嵌套关系决定了名字的查找顺序 而名称空间的嵌套关系是以函数定义阶段为准的, 即函数的嵌套关系与名字的查找顺序是在定义阶段就已经确定好的。
截止到函数运行之前
input = 111
def f1():
def f2():
print(input)
input = 222
f2()
input = 333
f1()
>>> 222
案例2
name = "小满"
def outer():
# 局部作用域
age = 3
print(f"name is {name} age is {age}")
# inner函数 : 既然是函数, 就是一串内存地址
def inner():
# 嵌套作用域
sex = "女"
print(f"name is {name} age is {age} sex is {sex}")
print(f"内部inner的内存地址 >>> {id(inner)}") # 打印inner 的内存地址
return inner # 返回的其实就是内层函数 inner 的内存地址
inner = outer()
print(f"外部inner的内存地址 >>> {id(inner)}")
inner()
name is 小满 age is 3
内部inner的内存地址 >>> 2187947076640
外部inner的内存地址 >>> 2187947076640
name is 小满 age is 3 sex is 女
闭包函数
大前提
闭包函数 = 名称空间与作用域 + 函数嵌套 + 函数对象
核心点:名字的查找关系是以函数定义阶段位为准
什么是闭包
“闭”函数指的是该函数是内嵌函数
“包”函数指的是该函数包含对外层函数作用域的引用(不是对全局作用域),就是不从自己这里找,从上一层函数找.
一言以蔽之:把你用一个麻袋装起来,你肚子饿了,只能从麻袋里面去找吃的.
实例:闭包函数之名称空间与作用域+函数嵌套
def f1():
x = 33333333
def f2():
print(x)
f2()
x = 11111
def bar():
x = 444444
f1()
def foo():
x = 2222222
bar()
foo()
>>> 33333333
实例:闭包函数之函数对象
def f1():
x = 33333333
def f2():
print('函数f2:', x)
return f2
f = f1()
print(f)
print(f.__name__)
def foo():
x = 66666
f()
print(f.__name__)
foo()
# 结果如下
<function f1.<locals>.f2 at 0x0000027C5B1CECB0>
f2
函数f2: 33333333
f2
让我们逐步解释这段代码:
def f1():
x = 33333333
def f2():
print('函数f2:', x)
return f2
f = f1()
print(f)
在这段代码中,我们定义了一个函数f1()
,并在其中定义了另一个函数f2()
。在f1()
函数内部,我们定义了变量x
并赋值为33333333
。然后,我们定义了嵌套函数f2()
,它打印出变量x
的值。
接下来,我们调用f1()
函数并将返回的函数对象赋值给变量f
。此时,f
引用了函数f2()
。
然后,我们打印变量f
的值。这里打印的是函数f2
的内存地址,而不是函数f2
的内部变量x
的值。
接下来,我们定义了另一个函数foo()
:
def foo():
x = 66666
f()
在foo()
函数中,我们定义了一个局部变量x
并赋值为66666
。然后,我们调用了之前保存在变量f
中的函数对象。
最后,我们调用foo()
函数:
foo()
这将触发函数f2()
的调用,并打印出函数f2: 33333333
。这里打印的是函数f2
内部变量x
的值。
所以,先打印出的是函数f2
的内存地址,然后再打印了f2
的内部变量x
的值。对于打印函数对象本身,可以通过打印f.__name__
来获取函数的名称。
为何要有闭包函数
闭包函数的应用
两种函数的体传参方式
直接把函数体需要的参数定义成形参
def f2(x):
print(x)
f2(1)
f2(2)
f2(3)
def f1():
x = 3
def f2():
print(x)
return f2
x = f1()
print(x)
x()
# 结果如下
<function f1.<locals>.f2 at 0x0000028297DAD790>
3
传参的方案1
import requests
def get(url):
response = requests.get(url)
print(len(response.text))
get('http://www.baidu.com')
get('http://www.cnbolgs.com/ccsvip')
# 结果如下
2381
481
传参的方式2
可以定义一个变量名,如果要重复获取更加直观
import requests
def outter(url):
def get():
response = requests.get(url)
print(len(response.text))
return get
baidu = outter('http://www.baidu.com')
baidu()
baidu()
baidu()
cnblogs = outter('http://www.cnbolgs.com/ccsvip')
cnblogs()
# 结果如下
2381
2381
2381
481
本文作者:小满三岁啦
本文链接:https://www.cnblogs.com/ccsvip/p/17887042.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。