命名空间(Namespace):是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的,它的键就是变量名,它的值就是那些变量的值。
注意: 大部分命名空间是字典;
python语言内置的名称,包括所有内置的函数等;
从解释器启动时创建,持续到解释器中止;
# 例如
import builtins
print(dir(builtins)) # 返回所有内置函数及异常名称等;
dir([object]):不带参数时,返回当前范围内的变量、方法和定义的类型列表;
带参数时,返回参数的属性、方法列表;
包含模块中定义的名称,记录了模块的变量、函数、类、其它导入的模块等
在模块被读入时创建,持续到解释器退出;
包含函数中定义的名称,记录了函数的变量、参数等
一个函数的局部命名空间在这个函数被调用时创建,持续到函数结束
注意: 任何函数都是先定义,再调用
globals():返回当前全局命名空间(字典)
locals():返回当前局部命名空间(字典)
# 例如
def outer():
def inner():
print(globals())
print(locals())
inner()
print(globals())
print(locals())
outer()
# 终端显示
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002C7ED5BEFD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'd:/shenlanclass/PythonFiles/day13/day1301.py', '__cached__': None, 'outer': <function outer at 0x000002C7ED4D71F0>} #inner全局变量
{} # inner局部变量
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002C7ED5BEFD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'd:/shenlanclass/PythonFiles/day13/day1301.py', '__cached__': None, 'outer': <function outer at 0x000002C7ED4D71F0>} # outer全局变量
{'inner': <function outer.<locals>.inner at 0x000002C7ED74C430>} # outer局部变量
可以发现,outer和inner的全局变量都是一样的,只有outer,那是因为inner是outer的嵌套函数,全局变量只考虑最外面的函数、变量、模块,类等等;
inner的局部变量是空字典,因为inner里面没有函数及变量
outer的局部变量有inner,是因为Inner是outer的嵌套函数
全局命名空间下,globals和locals返回的字典一致;
注意: 命名的查找空间顺序:局部命名空间→全局命名空间→内置命名空间
eval(expression[,globals[,locals]]):执行表达式,并返回执行结果,表达式只能是一行,
exec(object[,globals[,locals]]):执行表达式,无返回值,表达式可以是多行,
注意: 当globals和locals不传参数的时候,则按照默认顺序找参数,如果两者有指定的时候,只能在制定的空间查找,此操作不是inplace操作,
# 例如
eval('print(abs(-9))') # eval可以将字符串形式的表达式运行
print('----------------')
print(eval('print(abs(-9))')) #eval有返回值,返回后面的表达式,但是print返回none,因此输出none
print('----------------')
print(eval('abs(-9)')) # eval有返回值,对返回值输出
print('----------------')
def num1(a):
a = 999
def num2():
a = 10
b = 11
print(eval('a + b', {'a': 3, 'b': 4})) # 当eval上传globals参数时,只能从上传参数里面选数据
num2()
a = 0
b = 1
num1(a)
print('----------------')
def num1(a):
a = 999
def num2():
a = 10
b = 11
print(eval('a + b', {'a': 3, 'b': 4} ,{'a': 5, 'b': 6})) # # 当eval上传globals和locals参数时,只能从上传参数里面选数据
num2()
a = 0
b = 1
num1(a)
print('----------------')
# 终端显示
9
----------------
9
None
----------------
9
----------------
7
----------------
11
----------------
eval和exac的区别
# 例如
string1 = '''
a = 1
b = 2
print(a+b)
'''
string2 = '''
abs(-9)
'''
exec(string1) # exec可以运行多行语句
print(exec(string2)) #exec无返回值
# 终端显示
3
None
python可以直接访问命名空间的作用区域;
作用域分类:(作用域范围依次增大)
局部作用域:(Local)
闭包函数外的函数中:(Enclosing)
全局作用域:(Global)
内建作用域:(Built-in)
如果在当前函数作用域中找不到对应名称,则会向上一级去找,直到最后,如果找不到会报错;
只有模块,函数,类才会引入新的作用域,意思是,在全局作用域中,自定义函数,就会引入局部作用域;
闭包函数外的函数中需要满足三点要求:
1、需要是个嵌套函数;
2、外部函数的返回值是内部函数的引用/调用;
3、内部函数用到外部函数的变量(参数);
# 例如
def out1():
a = 1 # 这部分就是闭包函数外的函数中
b = 2 # 这部分就是闭包函数外的函数中
def in1():
a = 0
c = 5
c= a + b + d + e
print(c)
return in1()
d = 3
e = 6
out1()
# 终端显示
11
注意: 区分调用和引用
如果在局部作用域中,是没有办法对外部作用域的变量进行重新赋值的,此时我们可以用global和nonlocal实现;
global:对后面的全局变量重新赋值
nonlocal:对后面的闭包函数外的函数中变量重新赋值
# 例如
def out1():
a = 1
b = 2
d = 7
def in1():
# a = 0
# b = 5
global a
nonlocal b
a += 1
c= a + b + d + e
print(c)
return in1()
a = 3
b = 6
e = 8
out1()
print(a)
# 终端显示
21 # a=1,b=2,d = 7,e = 8
4 # 在局部变量中更改了全局变量a
注意:
# 例如
def out1():
a = 1
def in1():
a = a + b
print(a)
in1()
a = 0
b = 1
out1()
# 终端显示
UnboundLocalError: local variable 'a' referenced before assignment
(赋值前引用的局部变量“a”)
# 此时,in1()中,a = a + b语句,会认为两个a都是局部变量,不会认为一个是局部变量,一个是Enclosing变量,python检测到在局部变量中对a有定义,但是在执行a+b的时候,发现a还没有被定义,因为局部变量中有a,因此也不会调用Enclosing变量,此时陷入矛盾,就会报错,
解决方法,定义global或nonlocal
# 解决方案
def out1():
a = 1
def in1():
# global a #定义全局变量
a = 0 # 在局部变量中定义a,两者都可以解决
a = a + b
print(a)
in1()
a = 0
b = 1
out1()