Python函数——命名空间与闭包
前言
执行以下代码
def my_test(): x = 1 y = x+1 print(x) 》》 Traceback (most recent call last): File "C:/Users/jingjing/PycharmProjects/py3Project/路飞/第二模块/函数练习/斐波那契.py", line 36, in <module> print(x) NameError: name 'x' is not defined
x在函数里面定义了,为什么会报错?
python解释器如何执行
""" 从python解释器开始执行之后,就在内存中开辟了一个空间 每当遇到一个变量的时候,就把变量名和值之间的对应关系记录下来。 但是当遇到函数定义的时候解释器只是象征性的将函数名读入内存,表示知道这个函数的存在了,至于函数内部的变量和逻辑解释器根本不关心。 等执行到函数调用的时候,python解释器会再开辟一块内存来存储这个函数里的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。 """
结论:“存放变量与值的关系”的空间——叫做命名空间
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间
命名空间与作用域
命名空间的本质:存放变量与值的绑定关系
- 全局命名空间:创建的存储“变量名与值的关系”的空间叫做全局命名空间
- 局部命名空间:在函数的运行中开辟的临时的空间叫做局部命名空间
- 内置命名空间:内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。
三种命名空间之间的加载与取值顺序:
加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
使用:全局不能使用局部的,局部的可以使用全局的。
作用域
作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部名称空间,只能在局部范围内生效
globals方法:查看全局作用域的名字【print(globals())】
locals方法:查看局部作用域的名字【print(locals())】
def func(): a = 12 b = 20 print(locals()) print(globals()) func()
global关键字,在函数内部修改全局变量的值(在多人协作开发时慎用)
a = 10 print('原始值',a) def func(): global a a = 20 print('函数修改值', a) func() print('最终值',a) """ 原始值 10 函数修改值 20 最终值 20 全局变量被修改 """ a = 10 print('原始值',a) def func(): a = 20 print('函数修改值', a) func() print('最终值',a) """ 原始值 10 函数修改值 20 最终值 10 全局变量没有修改 """
nonlocal关键字:在嵌套函数中,
a = 1 print('原始值',a) def f1(): a = 2 print('进入f1内的打印', a) def f2(): a = 5 def f3(): nonlocal a # 修改局部的(当用nonlocal时),global a 修改全局的 a = 100 print('f3内的打印',a) f3() print('离开f2内的打印', a) # nonlocal修改的值只对 函数(存在变量a被修改)的范围内有影响 ,此处只对f2有效 f2() print('离开f1内的打印', a) f1() print('最终值', a) # nonlocal修改的值不影响全局的值 """ 原始值 1 进入f1内的打印 2 f3内的打印 100 离开f2内的打印 100 离开f1内的打印 2 最终值 1 """ a = 1 print('原始值',a) def f1(): a = 2 print('进入f1内的打印', a) def f2(): # a = 5 取消修改,nonlocal会对f1有影响 def f3(): nonlocal a # 修改局部的(当用nonlocal时),global a 修改全局的 a = 100 print('f3内的打印',a) f3() print('离开f2内的打印', a) # nonlocal修改的值只对 函数(存在变量a被修改)的范围内有影响,此处对f2和f1有效,因为a在f1中被修改,在f2中没有变化 f2() print('离开f1内的打印', a) f1() print('最终值', a) # nonlocal修改的值不影响全局的值 """ 原始值 1 进入f1内的打印 2 f3内的打印 100 离开f2内的打印 100 离开f1内的打印 100 最终值 1 """
函数作用域查找顺序
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
- locals 是函数内的名字空间,包括局部变量和形参
- enclosing 外部嵌套函数的名字空间
- globals 全局变量,函数定义所在模块的名字空间
- builtins 内置模块的名字空间
level = 'L0' n = 22 def func(): level = 'L1' n = 33 print(locals()) def outer(): n = 44 level = 'L2' print(locals(), n) def inner(): level = 'L3' print(locals(),n) # 此外打印的n是多少? inner() outer() """ 问题:在inner()里的打印的n的值是多少?44 取邻近函数outer中n=44 {'n': 33, 'level': 'L1'} {'level': 'L2', 'n': 44} 44 {'level': 'L3', 'n': 44} 44 """ func()
闭包
函数名的本质:就是函数的内存地址
def func(): print('func') a = func print(a) """ <function func at 0x0000000002357B70> # a指向了函数的内存地址 """
函数名可以作为函数的返回值
def func(): print("func") def func2(): print('func2') return '返回func2' return func2 # 函数名作为返回值 func2 = func() # 返回func2地址 print(func2) print(func2()) """ func <function func.<locals>.func2 at 0x0000000002567840> func2 返回func2 """
闭包的定义:内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数
# 闭包的常用形式: def func(): name = 'eva' def inner(): """闭包函数""" print(name) print(inner.__closure__) # 输出的__closure__有cell元素 :是闭包函数 return inner f = func() f() """ (<cell at 0x0000000000510C48: str object at 0x0000000002558B90>,) eva """
判断闭包函数的方法:__closure__
#输出的__closure__有cell元素 :是闭包函数 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #输出的__closure__为None :不是闭包函数,因为name为全局变量 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2()
闭包获取网络应用
from urllib.request import urlopen def outer(): url = "http://www.xiaohua100.cn/index.html" def inner(): return urlopen(url).read() return inner a = outer() content = a() print(content)
总结
作用域:小范围的可以用大范围的,但是大范围的不能用小范围的
范围从大到小(图)
如果在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的
如果在小范围内没有,就用上一级的,上一级没有的,就用上上级的,以此类推
如果都没有,报错
LEGB 代表名字查找顺序: locals -> enclosing function -> globals ->
函数名的本质:
就是一个变量,保存了函数所在的内存地址
闭包:
内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数