python高级之名称空间和作用域

名称空间与作用域

image

补充知识

栈区 / 堆区

  • 栈区 : 存放的是变量名与变量值的内存地址映射关系

  • 堆区 : 存放的是值真正的位置

image

名称空间

引入:

1.什么是名称空间?
-名称:定义的名字 空间:存放名字的地方
-名称空间即存放名字与对象映射/绑定关系的地方。
-名称空间只是虚拟的概念。栈区才是真正存在的。
2。名称空间有包含关系吗?
-名称空间之间本质是没有包含关系的,是互相独立的。
3.为什么要有名称空间?
-有了名称空间之后,就可以在栈区中存放相同的名字,让所有的名称不冲突。
4.查找名称空间的优先级是:
-在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,如果没有找到,再去全局作用域查找,还没有找到就在内置名称空间中查找,都没有找到就会抛出异常。
5.查找顺序:局部名称空间>>全局名称空间>>内置名称空间
6.三种全称空间的个数:内置、全局1只有一个。局部可以多个。

名称空间的分类(三类)

内建名称空间(也叫内置名称空间)

存放的名字:存放的是python解释器内置的名字
存活周期:python解释器启动则产生,python解释器关闭则销毁。
示例:
#例如:我们常用的内置函数
>>> len
>>> <built-in function len>
>>> print
>>> <built-in function print>
>>> input
>>> <built-in function input>

全局名称空间

存放的是顶级名字(文件级别的名字)
存活周期:在执行文件执行时生效,文件运行完毕或文件执行期间被删除则失效
x = 1 #全局名称空间
def foo(): #全局名称空间
y = 2
注意,"if"下面的定义的变量名都是全局的
if 1 > 0
z = 3 #全局
if 3 > 0:
p = 555 #全局

局部名称空间

存放函数内定义的名字
存活周期: 在调用函数时临时生效,函数调用完毕失效
示例一:
def f()
x = 222 #局部名称空间
f() #函数调用结束失效
示例二:
def func(a, b):
pass
下面👇调用了4次func,虽然使用pass语句,但是在调用函数时,也会产生局部名称空间,且它们每调用一次,都会产生一个新的局部名称空间。
func(10, 1)
func(10, 1)
func(10, 1)
func(10, 1)

三种名称空间的之间的关系

1.三种名称空间的加载顺序:
内置名称空间= > 全局名称空间= > 局部名称空间
2.三种名称空间中一定要有的名称空间:
内置名称空间、全局名称空间
3.三种名称空间的销毁顺序:
局部名称空间 =》 全局名称空间 =》 内置名称空间
4.三种名称空间的名字的查找优先级:在当前所在的位置,一层层向上查找
如果当前在局部名称空间:
局部名称空间 ==> 全局名称空间 ==> 内置名称空间
## 三种名称空间的名字的查找优先级示例
如果当前在局部名称空间:局部名称空间 => 全局名称空间 => 内置名称空间
input = 100
def func():
input = 10
print(input) # 10
func()
#ps: 取值顺序和加载顺序无关
如果当前在全局名称空间:全局名称空间 --> 内置名称空间
示范一:
input = 111 #1.在全局定义input = 111
def func(): #2.定义f1函数
input = 222 #4.在func局部名称空间内定义input = 222
func() #3.调用func,执行func函数体代码
print(input) #5.在当前名称空间内取值,此时取值为111
111
示范二:
x = 222 #
def func():
print(x) # 111
x = 111 # 定义阶段print语句还没有执行,也就是说func中的名称空间还没有产生。当运行到这条代码,x与之前值222的内存地址的绑定关系解除。绑定了新的值111的内存地址。所以当调用func()时,func中的局部名称空间才产生,这个时候print在自己的名称空间中没有找到,跑到全局找到了x=111,所以上面返回111
func()
示范三(难点):名称空间的"嵌套"关系是以函数定义时为准的,与调用位置无关。
x = 111 # 1、在全局定义x = 111
def func(): # 2、定义func函数,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
print(x) # 7、打印x,x此时从全局取值,结果为111
def foo(): # 3、定义foo函数
x = 222 # 5、在foo的局部名称空间内定义x = 222,并不会干扰到全局的x
func() # 6、调用func
foo() # 4、调用foo
111
示范四(难点):函数的嵌套定义
input = 111
def f1():
def f2():
print(input) # 222
input = 222
f2()
f1()
示范五(难点):逻辑错误
x = 111
def func():
print(x) # 报错(UnboundLocalError: local variable 'x' referenced before assignment)。
x = 222
func()

名称空间总结与注意事项

三种名称空间必须要有的是 : 内置名称空间, 全局名称空间

重要概念:名称空间的嵌套关系是在函数定义阶段(检测语法)时确定的,与函数调用的位置无关,与函数定义位置有关。函数的取值位置是个很绕的知识点,最好一步一步分析函数执行步骤,与惯性思维作斗争。
## 示例一
代码一:
x = 111 # 1、首先在全局定义x = 111
def func(): # 2、定义func,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
print(x) # 5、x向全局取值,此时x = 222,所以最终结果为222。
x = 222 # 3、此时全局定义x = 222
func() # 4、调用func
## 222
代码二:
x = 111 # 1、首先在全局定义x = 111
def func(): # 2、定义func,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
print(x) # 4、x向全局取值,此时x = 111,所以最终结果为111。
func() # 3、调用func。
x = 222 # 5、此时在全局定义x = 222,但func函数已经调用,函数内的x已经取值为111。
## 111
示例二
x = 1 # 1、在全局定义x = 1
def func(): # 2、定义func函数,此时已确定x取值先从func局部取值,局部未找到则向全局取值。
print(x) # 7、打印x,x此时从全局取值,结果为1
def foo(): # 3、定义foo函数
x = 222 # 5、在foo的局部名称空间内定义x = 222,并不会干扰到全局的x
func() # 6、调用func
foo() # 4、调用foo
# 1
示例三:
input = 111 # 1、在全局定义input = 111。
def f1(): # 2、定义f1函数。
def f2(): # 4、在f1局部名称空间内定义f2。
input = 333 # 7、在f2局部名称空间内定义input = 333。
print(input) # 8、input先在当前所在名称空间内取值,此时input值为333。
input = 222 # 5、在f1局部名称空间内定义。
f2() # 6、调用f2,执行函数体代码。
f1() # 3、调用f1,执行f1函数体代码。
# 333
示例四
count = 1
def func():
count = 100
print(count) # 直接在当前名称空间取值,count为100。
func()
# 100
示例五:
def f1():
m=111
return m
def f2():
print(res) # 此时已确定res取值先从f2局部取值,局部未找到则向全局取值。
def f3():
print(res) # 此时已确定res取值先从f3局部取值,局部未找到则向全局取值。
res=f1() # f1() = m = 111
f2()
f3()
# 111
# 111
示例六:
x=111
def f1():
print(x) # 定义阶段已经确定先从局部取值,但是局部x是先取值再定义,所以保存
x = 222
f1()
# UnboundLocalError: local variable 'x' referenced before assignment
示例七:
m = [111,]
def func(y):
m.append(333)
print(y)
func(m)
m.append(222)
print(m)
# [111, 333]
# [111, 333, 222]

作用域

什么是作用域?
-作用域就是根据名称空间的范围和特点的不同进一步做了归类。
-查看作用域:: globals( ), locals( )
-变量的生效范围,分为全局作用域和局部作用域。

全局作用域与局部作用域

全局作用域

-包含:全局名称空间、内置名称空间
-特点:全局存活,全局有效
x就属于全局名称空间,在全局名称空间和局部名称空间都能访问到。
x = 10
def f1():
print(x)
print(x)
f1()
# 10
# 10
globals() ## 返回包含当前范围的全局变量的字典。
x = 111
print(globals())
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000002061940>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/desktop/code.py', '__cached__': None, 'x': 111}

局部作用域

-包含:局部名称空间
-特点:临时存活,局部有效
局部作用域可以引用全局作用域的变量,但不可以改变
x = 10
def f1():
x = 20
f1()
print(x) # 10
locals() ## 返回包含当前作用域的局部变量的字典。
x = 111
def func():
a = 10
b = 20
print(locals()) # {'a': 10, 'b': 20}
func()

global与nonlocal关键字

global关键字

-使用方式:global x:声明这个x是全局的名字x(x也可以是其他变量)
-针对的是不可变类型的值
-只能在局部名称空间使用。在局部声明一个全局作用域的变量。在 global 语句中列出的名称不得在同一代码块内该 global 语句之前的位置中使用。
-global用处:如果想在局部修改全局的名字对于的值,且对应得一定要是不可变类的值,这个时候就使用global。(注意:global作用就是争对局部中修改全局中不可变类型的值)
代码一:
在全局空间定义一个变量"x",函数"foo",在函数内部修改"x"的值
x = 111
def foo():
x = 222
foo()
print(x) #111
#发现并无软用
代码二:
使用"global"关键字之后
x = 111
def foo():
global x # 声明下面的"x"属于全局
x = 222
foo()
print(x) #222
#发现成功了
global 语句中列出的名称不得被定义为正式形参,不也得出现于 for 循环的控制目标、class 定义、函数定义、import 语句或变量标注之中。
局部名称空间不能修改全局名称空间的不可变数据类型的值,只能引用。
c = 1
def func():
c += 1 # 不可更改,在更改时会从先局部名称空间取c的值,然后再赋值给c,造成先引用、后定义的保错
print(c)
func()
## local variable 'count' referenced before assignment
对于可变类型,也不能使用先取值后修改再赋值给原变量名这种方式,而是使用内置方法来修改。
代码一:
c = [1,2,3]
def func():
c += [3,4,5] # 相当于 c = c + [3,4,5]
print(c)
func()
## UnboundLocalError: local variable 'c' referenced before assignment
代码二:
c = [1,2,3]
def func():
c.extend([3,4,5])
print(c)
func()
## [1, 2, 3, 3, 4, 5]
使用global关键字,可以在局部修改一个全局变量。
c = 1
def func():
global c #声明下面的c属于全局
c += 1
print(c)
func()
# 2

nonlocal关键字

-使用方法 : nonlocal x
-作用:修改该函数外层函数包含的变量名对应的值, 也是针对不可变类型(nonlocal起码在第二次嵌套函数内使用才有意义,能改变一个外层函数的非全局变量)
-注意 : 只能在局部名称空间中查找, 先从外部嵌套的函数找, 没找到再往上找, 如果都没找到则报错,不会去全局修改变量值
代码一:
不做任何处理,此函数应该是打印"f2"函数下的"x = 333"
x = 111
def f1():
x = 222
def f2():
x = 333
def f3():
x = 444
f3()
print(x) # 333
f2()
f1()
print(x) # 111
代码二:
使用"nonlocal"关键字,nonlocal起码在第二次嵌套函数内使用才有意义,能改变一个外层函数的非全局变量
x = 111
def f1():
x = 222
def f2():
x = 333
def f3():
nonlocal x #将下面的"x = 444"变成了上一层的"x"的值(ps:逻辑上是x=333 被修改为x=444,)
x = 444
f3()
print(x) # 444
f2()
f1()
代码三:
如果外层函数都没有"x"这个值,报错
x = 111 #并不会去全局修改
def f1():
def f2():
def f3():
nonlocal x
x = 444
f3()
print(x)
f2()
f1() #报错,SyntaxError: no binding for nonlocal 'x' found
只能在函数嵌套定义的内层函数中使用,在第一层局部名称空间内使用会报错。用作函数嵌套定义中,在内层函数中声明一个外层局部名称空间的变量。
nonlocal 语句中列出的名称不得与之前存在于局部作用域中的绑定相冲突。
x = 111
def f1():
x = 222
def f2():
nonlocal x
x = 333
f2()
print(x) # 333
f1()

LEGB原则

-按照LEGB原则就近取值,取值顺序单向不可逆。
-LEGB代表名字查找顺序 :Local本地 --> Enclosed嵌套函数的外层函数内部 --> Global全局 -->Builtin内置
L ——Local(function); 函数内的名字空间
E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G—— Global(module); 函数定义所在模块(文件)的名字空间
B —— Builtin(Python); Python内置模块的名字空间
(从局部开始找时)局部名称空间 --> 全局名称空间 --> 内置名称空间
name = 'xiao'
def index():
name = 'quan'
age = 18
print(name)
print(f"这是局部的变量 :>>>> {locals()}")
index()
print(f"这是外部的变量 :>>>> {globals()}")
posted @   Xiao0101  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示

目录