前言
执行以下代码
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 -> builtins
函数名的本质:
就是一个变量,保存了函数所在的内存地址
闭包:
内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数