09--函数的名称空间与作用域
1.名称空间
名称空间(namespaces):存放名字的地方,是对栈区的划分。有了名称空间之后,就可以在栈区中存放相同的名字,不会冲突。
详细的名称空间:分为三种:
1.1 内置名称空间
# 存放的名字:存放的python解释器内置的名字
>>> print
<built-in function print>
>>> input
<built-in function input>
# 存活周期:python解释器启动则产生,python解释器关闭则销毁
1.2 全局名称空间
# 存放的名字:只要不是函数内定义(局部名称空间)、也不是内置的(内置名称空间),剩下的都是全局名称空间的名字。
# 存活周期:python文件执行则产生,python文件运行完毕后销毁
# 以下都是全局名称空间:
import os # os
x=10 # x
if 13 > 3: # y, z
y=20
if 3 == 3:
z=30
# func=函数的内存地址
def func(): # func
a=111
b=222
class Foo: # Foo
pass
1.3 局部名称空间
注意:局部名称空间,一定是函数调用时产生,定义函数时不会产生局部空间
# 存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
# 存活周期:在调用函数时存活,函数调用完毕后则销毁
def func(a,b):
pass
# 函数调用一次,都产生一个局部空间,下面是四个局部空间
func(10,1)
func(11,12)
func(13,14)
func(15,16)
1.4 名称空间的顺序
总结:定义阶段:就是申明各种名字,调用阶段:就是使用各种名字。
所以,调用时,查找名字的顺序,就是根据定义阶段产生的各种名字,一层一层的去找。
a.名称空间的加载顺序:
内置名称空间>全局名称空间>局部名称空间
b.销毁顺序:
局部名称空间>全局名空间>内置名称空间
c.名字的查找优先级:当前所在的位置向上一层一层查找:
内置名称空间
全局名称空间
局部名称空间
# 如果当前在局部名称空间:
局部名称空间—>全局名称空间->内置名称空间
input=333 # 全局input
def func():
input=444
print(input) # 局部input
func()
# 如果当前在全局名称空间
全局名称空间->内置名称空间
input=333
def func():
input=444
func()
print(input) # 全局input
# 示范1:查找顺序,不是单纯的在其之前的代码去寻找(是在其空间有就行)
def func():
print(x)
x=111
func() # 111
# 示范2:名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关
无论函数在哪调用,都要回到定义函数阶段去看,变量引用在哪个名称空间
x=1 # 可以在这个位置
def func():
print(x) # 定义阶段:函数func内没有局部x,就只能全局去找。
x=1 # 可以在这个位置
def foo():
x=222
func()
x=1 # 可以在这个位置
foo() # 1
解析:x= 1 可以在调用函数前的任意全局位置,没有的话就会报错;
永远也不会打印 foo局部内的 x= 222。因为定义阶段时,函数func内没有局部x,就只能全局去找。
# 示范3:函数嵌套定义
input=111
def f1():
def f2():
input=333
print(input)
input=222
f2()
f1() # 333-->222-->111-->内置
# 示范4:
x=111
def func():
print(x)
x=222
func()
# 报错,UnboundLocalError:
local variable 'x' referenced before assignment。绑定错误,说x=222,应当在print(x)之前
注意!!!
# 示例一:
x = 1
def func1():
print(x)
x = 3
func1() # 3
# 示例二:
y = 1
def func2(m=y):
print(m)
y = 3
func2() # 1
# 分析:为什么两个示例,结果不一样,有啥区别?
:示例一中,func1 定义阶段时,只是申明了func1 的名称空间;
函数体内的print(X) 是在func1调用阶段时执行,在其名称空间一层层寻找。
此时,名称空间里的x 是绑定3 的内存地址,故结果为3。
(只是可以这样理解,但实际是函数被调用时,才会产生局部名称空间)
:示例二中,func2 定义阶段时,申明了func2 和 其参数 m 的名称空间,
并且 参数m 已经绑定y的内存地址(此时,y是1的内存地址);
而函数体内的print(m) 是在func2调用阶段时执行,此时 m 仍为 1 的内存地址,故结果为1。
后续的 y = 3,就根本m 没有任何关系,因为在func2 定义阶段时,就已经将 m 绑定为 1 的内存地址了。
:实际上的原因是:默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的是值的内存地址
2.作用域--->作用范围
# a.全局作用域:内置名称空间、全局名称空间
1.全局存活
2.全局有效:被所有函数共享
x=111
def foo():
print(x,id(x))
def bar():
print(x,id(x))
foo()
bar()
print(x,id(x))
# b.局部作用域: 局部名称空间的名字
1。临时存活
2。局部有效:函数内有效
def foo(x):
def f1():
def f2():
print(x)
# LEGB
# builtin
# global
def f1():
# enclosing
def f2():
# enclosing
def f3():
# local
pass
global 与 nonlocal
########### global
# 示范1:
x=111
def func():
x=222
func()
print(x) # 111
# 示范2:如果在局部想要修改全局的名字对应的值(不可变类型),需要用global
x=111
def func():
global x # 声明x这个名字是全局的名字,不要再造新的名字了
x=222
func()
print(x) # 222
# 示范3:若是在局部修改全局的可变类型值,不用使用global
l=[111,222]
def func():
l.append(333)
func()
print(l) # [111, 222, 333]
############ nonlocal:
从函数外层函数中一层一层去找,局部内的(不会找到全局去)
# nonlocal: 修改函数外层函数包含的名字对应的值(不可变类型)
x=0
def f1():
x=11
def f2():
nonlocal x
x=22
f2()
print('f1内的x:',x)
f1() # f1内的x:22
def f1():
x=[]
def f2():
x.append(1111)
f2()
print('f1内的x:',x)
f1() # [1111]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构