12. 函数的名称空间与作用域
1. 名称空间
1.1 概念
名称空间是存放变量名与变量值映射关系的地方
1.2 分类
1.2.1 内置名称空间
Python解释器运行而产生的名称空间,如def、if、else、len
Python解释器运行---产生
Python解释器关闭---释放
1.2.2 全局名称空间
在python文件中编写的代码运行产生的名字都会存到全局名称空间(函数体代码之外的)
name = 'LeoMessi' # 变量名name存入全局名称空间
def login(): # 函数名login存入全局名称空间
pass
if True:
a = 111 # 变量名a存入全局名称空间
for i in range(10): # 变量名i存入全局名称空间
pass
while True:
b = 222 # 变量名b存入全局名称空间
python文件开始运行 产生
python文件运行结束 释放
1.2.3 局部名称空间
函数体代码运行产生的都是局部名称空间
def register():
name = 'ronaldo' # name存入局部名称空间
函数体代码开始运行 产生
函数体代码运行结束 释放
1.3 名称空间的查找顺序
在查找名字的时候,一定要先搞清楚当前在哪个空间
1. 如果在局部名称空间
局部名称空间————> 全局名称空间————> 内置名称空间
2. 如果在全局名称空间
全局名称空间————> 内置名称空间
(只能向外层不能向里层)
3.代码执行顺序
# 局部名称空间之间如果不是嵌套关系,那么互不干涉
def f1():
name = 'LeoMessi'
def f2():
age = 35
print(name) # 报错not defined
f1()
f2()
2. 作用域
2.1 概念
作用域就是名称空间能够作用的范围,即变量名和变量值可以被访问的范围(变量名定义的位置和可以查找的位置)
2.2 分类
内置作用域 Built-in
Built-in(内置):解释器内置的变量,比如int, str等。
作用范围:所有模块(文件)
Python中没有块级作用域。
块级作用域:代码块中的变量,比如if、while、for后面的代码
程序任意阶段、任意位置均可使用(全局有效)
全局作用域 Global
Global(全局):一般模块文件顶层声明的变量具有全局作用域,从外部来看,模块的全局变量就是一个模块对象的属性,仅限于单个模块文件中。
作用范围:当前模块(文件)
程序任意阶段、任意位置均可使用(全局有效)
局部作用域 Local
Local(局部变量):暂时的存在,依赖于创建该局部作用域的函数。
函数存在,则局部变量存在;函数不存在,则局部变量不存在。
作用范围:当前整个函数体范围
一般情况下,只在各自局部名称空间中有效(局部有效)
内嵌作用域 Enclosed
Enclosed(嵌套):一般是在函数中嵌套函数的时候,外层函数的变量作用域。
作用范围:闭包函数
2.3 作用域举例
内置作用域举例:
内建作用域里面提前加载好的 如os
import os
全局作用域举例:
自己在文件中定义的变量
num = 123
局部作用域与内嵌作用域举例:
age = 18
# 局部作用域:定义在函数或者类内部的所有变量
# 内嵌作用域:在函数中嵌套函数的时候,外层函数的变量作用域。
def student():
age = 28 # 这里的age对student函数是局部作用域,对inner函数是内嵌作用域
print(f" student my age is {age}")
def inner():
age = 38 # 这里的age为inner函数的局部作用域
print(f"inner my age is {age}")
inner()
student()
print(f"global my age is {age}")
作用域的加载顺序:内置 -- 全局 -- 外层 -- 里层
查找循序 : 里层 -- 外层 -- 全局 -- 内置
查找顺序遵循一个规则 LEGB 规则
L ---> local 局部的局部 最里层的函数
E ---> enclosed 内嵌,相对里层嵌套函数的外层
G ---> global 全局
B ---> built-in 内建
2.4 作用域声明
# 字典是可变数据类型 ,大家对于修改字典的时候都是修改的同一块内容空间地址上面的值
# 【总结】
# 【1】名称空间就是存放变量名和变量值映射关系的地方 内建 全局 局部
# 【2】作用域就是变量名和变量值映射关系存放的范围 内建 全局 局部 内嵌
# 【3】如果局部修改全局不可变数据类型 用 global 提高当前作用域级别
# 如果是内嵌修改局部不可变数据类型 用nonlocal 提高作用域级别
# 内嵌作用域无法修改全局作用域中的不可变数据类型
2.5 局部修改全局不可变数据类型
局部修改全局可变数据类型---不需要任何处理---可变数据类型的内存地址不会改变
不可变数据类型---给一个不可变数据类型重新赋值
局部在一般情况下,不能修改全局不可变类型
a = 666
print(a, id(a)) # 666 2877912118896
def change_num():
a = 999
print(a, id(a)) # 999 2877912118960
change_num()
print(a, id(a)) # 666 2877912118896
局部使用关键字 global 修改全局不可变类型
a = 666
print(a, id(a)) # 666 1772611630704
def change_num():
global a # 在局部使用关键字改变了全局变量a
a = 999
print(a, id(a)) # 999 1772611630768
change_num()
print(a, id(a)) # 999 1772611630768
局部直接修改全局可变数据类型
a = [666]
print(a, type(a), id(a)) # [666] <class 'list'> 2664527109952
def change_num():
a.append(999) # 局部修改全局的可变类型,内存地址不发生变化
print(a, id(a)) # [666, 999] 2664527109952
change_num()
print(a, id(a)) # [666, 999] 2664527109952
2.6 嵌套函数(里层)修改全局不可变数据类型
里层函数无法直接修改全局不可变类型
a = 666
print(a, id(a)) # 666 2409063970416
def change_num():
def inner():
a = 999
print('里层函数', a, id(a)) # 里层函数 999 2409063970480
inner()
change_num()
print(a, id(a)) # 666 2409063970416
里层函数可使用关键字 global 修改全局不可变类型(外层无相同变量名)
a = 666
print(a, id(a)) # 666 2892394985072
def change_num():
def inner():
global a # 里层使用global对全局不可变实现了修改
a = 999
print('里层函数', a, id(a)) # 里层函数 999 2892394985136
inner()
change_num()
print(a, id(a)) # 999 2892394985136
里层函数使用关键字 global 修改全局不可变类型(外层有相同变量名)
a = 666
print(a, id(a)) # 666 2465023850096
def change_num():
a = 111
print(a, id(a)) # 111 2465022807728
def inner():
global a # global使该函数以下所有的a都成为了全局a
print('里层函数', a, id(a)) # 里层函数 666 2465023850096
a = 999
print('里层函数', a, id(a)) # 里层函数 999 2465023850160
inner()
change_num()
print(a, id(a)) # 999 2465023850160
里层函数使用 nonlocal 关键字修改外层函数不可变类型
a = 666
print(a, id(a)) # 666 2836141569648
def change_num():
a = 111
print(a, id(a)) # 111 2836140527280
def inner():
nonlocal a
print('里层函数', a, id(a)) # nonlocal使得里层函数与外层函数的a保持一致
# 里层函数 111 2836140527280
a = 999
print('里层函数', a, id(a)) # 里层函数 999 2836141569712
inner()
change_num()
print(a, id(a)) # 666 2836141569648
2.7 global与nonlocal小结
global声明全局变量,加载到的是全局的不可变数据类型
nonlocal声明里层函数的外层函数变量,加载到的是里层函数的外层函数变量