名称空间与其查找顺序、作用域、global与nonlocal关键字的使用、函数名的多种用法以及函数的嵌套
今日内容总结
在昨日的学习中,我们初步了解了函数,以及函数的使用,参数的介绍。而昨日的重点正是参数。今日的学习,依旧是函数,学习函数的用法。
名称空间
名称空间,其实就是存放变量名与变量值绑定关系的地方。在程序执行期间最多会存在三种名称空间。分别是内置名称空间,全局名称空间,局部名称空间。接下来,就是对这三种名称空间的介绍。
内置名称空间。其实就是python解释器提前给我们定义好的,内置函数名与函数体代码绑定的空间。因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名len(),print()等。
全局名称空间。我们在python文件中编写的代码运行产生的名字都会存到全局名称空间。是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中。例如:
# 全局名称空间
name = 'jason'
def output():
pass
if i < 3:
i = 1
for i in range(5):
pass
while i < 3:
i = 1
# 以上类型中,无论是函数名与函数体的关系,还是变量名与值的关系。都是在全局空间的
局部名称空间。就是函数体代码运行产生的空间。函数的形参、函数内定义的名字都会被存放于该名称空间中,只要在函数内的名称空间就是局部的。例如:
# 局部名称空间
def output():
name = 'jason'
# 如以上这种,在函数体代码中产生的变量名与变量值之间对应的关系,都是存放在局部名称空间的
生命周期。
1.内置名称空间:在python解释器启动时创建,在解释器关闭时销毁。
2.全局名称空间:在执行文件时,创建全局名称空间,所有文件中的代码全部执行完毕后,销毁名称空间(即解释器关闭时)
3.局部名称空间:调用函数时创建,函数执行完毕时就销毁
名字的查找顺序
名称空间是用来存放名字与值绑定关系的地方,而我们的名称空间分了三类,在查找的时候,就有查找顺序的出现。查找顺序如下:
在局部名称空间的查找顺序:局部名称空间-->全局名称空间-->内置名称空间
在全局名称空间时的查找顺序:全局名称空间-->内置名称空间
当我们在查找名字时,一定要先搞清楚自己在哪个空间,因为不同的名称空间时可以有相同的名字的。如:
# 当三个名称空间出现相同的变量名时,判断我们打印的到底是哪个空间的变量名
len = '全局空间'
def output():
len = '局部空间'
print(len)
output() # 局部空间
print(len) # 全局空间
# 因为我们说过,调用函数,函数体才会执行,而函数体执行,就会产生局部名称空间。为了更方便理解,我们用了这样一个嵌套函数来举例
x = 1 # 全局名称空间
def a1(): # 定义一个函数a1
x = 2 # 局部名称空间1
def a2(): # 定义一个函数a2
x = 3 # 局部名称空间2
def a3(): # 定义一个函数a3
x = 4 # 局部名称空间3
print(x) # 打印x
a3() # 调用a3
a2() # 调用a2
a1() # 调用a1
# 以上代码,看似很复杂,但是我们之前说过,在查找名字时,一定要搞清楚自己所在的空间。以上代码相当于,在全局空间内有一个空间为局部名称空间1,而局部名称空间1内有一个局部名称空间2.局部名称空间2内有一个局部名称空间3。当我们不调用函数a1时。x = 1,而调用函数a1后,a1中的函数体代码开始执行。执行顺序为:首先,进入局部名称空间1,x = 2。随后调用函数a2,随后进入局部名称空间2,x = 3。随后调用函数a3,随后进入局部名称空间3,这时候,x = 4了,最后打印4。这样的顺序一定要知道。
# 但是,如果我们a3中的函数体代码如下
def a3():
print(x)
x = 4
# 此时调用函数a1会报错提示local variable 'x' referenced before assignment。因为我们在检测语法时,发现f3的局部名称空间将来会有x,所以查找的时候就跟a3的局部名称空间要。这种特例只是为了理解名字查找顺序的。一般不会出现
作用域
作用域就是名称空间能够作用的范围。
# 内置名称空间:程序任意阶段任意位置均可使用(全局有效)
# 全局名称空间:程序任意阶段任意位置均可使用(全局有效)
# 局部名称空间:一般情况下只在各自局部名称空间中有效(局部有效)
global与nonlocal关键字
gloabl关键字:局部修改全局不可变类型。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用。如:
# global关键字的使用
age = 18
def func2():
# 明确声明使用全局的变量age(修改a的值)
global age
age = 19
func2()
print(age) # 19
# 而如果我们将global注释掉会发生什么呢
age = 18
def func2():
# 明确声明使用全局的变量age(修改a的值)
# global age
age = 19
func2()
print(age) # 18
# 这里依旧是我们查找名字顺序,如果还不明白的话,再看看上面的解释
nonlocal关键字:nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。
# nonlocal关键字的使用
a = 1
def func3():
a = 10
def inner():
# 明确声明 要使用上一层的 a 如果上一层没有,则使用上上一层,但是不能使用全局中的a
nonlocal a
a = 100
print(a)
inner()
print('这是func3中的a', a)
func3() # 100 这是func3中的a 100
# 局部名称空间嵌套的情况下,内层如果想要修改外层。情况1数据是不可变类型:需要使用关键字nonlocal。情况2数据是可变类型:不需要使用关键字nonlocal。
函数名的多种用法
函数名,类似于变量名,同时,函数名也有多种用法:
1.函数名可以当做变量名赋值
def output():
print('jason nb')
print(output) # <function output at 0x0000018FB5941EA0>
res = output
print(res) # <function output at 0x00000203ED5E1EA0>
res() # jason nb
# 当函数名做变量名赋值时,在上述示例中,是让res也指向函数体代码。这时候res() = output()
2.函数名可以当成函数的实参
def output():
print('jason nb')
def func(a):
print('jason handsome')
print(a)
a()
func(output) # jason handsome <function output at 0x0000021FEF581EA0> jason nb
# 当我们的函数名当做别的函数的实参后,如上述代码中,可以在func调用a()。指向的就是实参的函数
用法3:函数名可以当做函数的返回值
def output():
print('jason nb')
return func
def func():
print('jason handsome')
res = output() # jason nb
print(res) # <function func at 0x000001D0D910BC80>
res() # jason handsome
# 将函数名func当做返回值后,res接收函数名。res() = func()
用法4:函数名可以作为容器类型的元素
def output():
print('jason nb')
a = ['jason', 'nb', 666, output]
print(a) # 'jason', 'nb', 666, <function output at 0x0000017090501EA0>]
a[-1]() # jason nb
# 容器类型:内部可以存档多个元素的数据类型>>>:列表、元组、字典
函数的嵌套
函数的嵌套,就是说在函数中再定义函数,或者说,把一个函数定义在另一个函数的内部。嵌套函数是为函数内部服务的,比如减少代码的重复,想要调用函数,要使用函数名,内函数也一样。如果不用函数名调用内函数,内函数就永远不会执行。在上述的代码示例中我们提到过。对于函数的嵌套使用,只要搞清楚一个道理。那就是,函数只有在被调用的时候,才会执行函数体代码。也就是说,就算你嵌套了函数,如果不调用嵌套函数的话,嵌套了跟没嵌套一样。因为它不会执行。

学习内容总结
浙公网安备 33010602011771号