本文讲述了python内置函数locals和globals以及与之相关的知识点。
Python内置函数locals()和globals()
Python内置函数locals()和globals()主要提供基于字典的访问局部变量和全局变量的方式。
名字空间
在理解这两个函数之前,首先要先理解一下python中的名字空间的概念。
- 1.python使用叫做名字空间的对象来记录变量的轨迹。
- 2.名字空间是一个字典,它的键就是字符串形式的变量名称,它的值就是变量的实际值。
- 3.名字空间可以像python的dictionary一样进行访问。
4.在一个python程序中的任何一个地方,都存在几个可用的名字空间。
- 每个函数都有着自己的名字空间,叫做局部名字空间。它记录了函数的变量,包括函数的参数和局部定义的变量。
- 每个模块拥有它自己的名字空间,叫做全局名字空间。它记录了模块的变量,包括函数、类、其他导入的模块、模块级的变量和常量。
- 还有内置名字空间,任何模块均可访问它。它存放着内置的函数和异常。
5.当一行代码要使用变量x的值时,Python会到所有可用的名字空间去查找变量,按照如下顺序:
- 局部名字空间-特指当前函数或类的方法,如果函数定义了一个局部变量x或者是一个参数x,python将使用它,然后停止搜索。
- 全局名字空间-特指当前的模块,如果模块定义了一个名为x的变量,函数或类,python将使用它,然后停止搜索。
- 内置名字空间-对每个模块都是全局的。作为最后的尝试,python将假设x是内置函数或变量。
- 如果python在这些名字空间找不到x,它将放弃查找并引发一个
NameError
异常,同时传递name 'x' is not defined
这样一条信息。
6.名字空间在运行时直接可以访问。局部名字空间可以通过内置的
locals()
函数来访问。全局(模块级别)名字空间可以通过内置的globals()
函数来访问。locals()
函数对局部(函数)名字空间做了些什么,globals()
函数就对全局(模块)名字空间做了什么。globals()
要更强大一些,因为一个模块的名字空间包含了模块级的变量和常量,它还包括了所有在模块中定义的函数和类,以及任何被导入到模块中的东西。
解释from module import *和import module之间为什么不同
1.使用上的区别
- 如果采用的是
from module import *
,则可在代码中直接使用方法名 - 如果采用
import module
,则在代码中必须写module.function
的形式 - 举个例子,
from math import sqrt
是把sqrt
作为本文件的方法导入进来了,使用的时候只需要直接调用sqrt。而如果导入方式是import math
,那么调用的时候要采用math.sqrt
的方式。
- 如果采用的是
2.使用上为什么有区别?
- 导入
import module
,即模块自身被导入,但是它保持着自己的名字空间。此时就需要使用模块名来访问它的函数或属性,即module.functon
- 导入
from module import *
,即从另一个模块中将指定的函数和属性导入到你自己的名字空间,此时你就可以直接访问它们而不需要引用它们所来源的模块。使用globals()
函数观察一下,我们会更加深刻的理解这两者的区别。
- 导入
3.用globals()函数来直观的解释两者的区别(以
math
模块为例)先看导入
import math
的情况
# coding:utf-8
import math
def foo(x):
return math.sqrt(x)
此时输入globals()
查看当前位置的全部全局变量,查看到的内容如下:
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__':<class'_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'D:/PythonWorkSpace/GlobalsTest/GlobalsTest1.py',
'math': <module 'math' (built-in)>,
'foo': <function foo at 0x000001C512FE1268>}
其中的'math': <module 'math' (built-in)>
表明此时导入进来的是math
模块自身,如果需要使用sqrt,则需要从math模块里面取。
再看导入from math import sqrt
的情况
# coding:utf-8
from math import sqrt
def foo1(y):
return sqrt(y)
此时输入globals()查看当前位置的全部全局变量,查看到的内容如下:
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class'_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'D:/PythonWorkSpace/GlobalsTest/GlobalsTest2.py',
'sqrt': <built-in function sqrt>,
'foo1': <function foo1 at 0x000001B916FB1268>}
可以看到其中的'sqrt': <built-in function sqrt>
表明导入进来的是sqrt()
这个函数,此时就可以直接使用该函数了。
locals()函数与globals函数
- 1.locals()实例,该例子可以表明我们可以通过
locals()
函数获取当前位置的包括参数在内的所有局部变量。
# coding:utf-8
def foo1(arg,a):
x = 100
y = 'Hello python!'
for item in range(10):
j = 1
k = item
print(locals())
foo1(1,2)
上述例子运行结果为:
{‘k’: 9, ‘j’: 1, ‘item’: 9, ‘y’: ‘Hello python!’, ‘x’: 100, ‘a’: 2, ‘arg’: 1}
2.locals()是只读的,不可修改,而globals可以修改。下面来解释原因。
- locals()实际上并没有返回局部名字空间,它返回的是一个拷贝。所以对它进行修改,修改的是拷贝,而对实际的局部名字空间中的变量并没有什么影响。
- globals()与locals()函数的行为完全相反,它返回的是实际的全局名字空间,而不是一个拷贝。所以对globals()所返回的dictionary的任何改动都会直接影响到全局变量的取值。
- 3.下面将给出实例来说明上述问题。
# coding:utf-8
z = 7 #定义一个全局变量
def foo(arg):
x = 1
print(locals())
print('x = ',x)
locals()['x'] = 2 #修改的是局部名字空间的拷贝,而实际的局部名字空间中的变量并未受到影响
print(locals())
print('x = ',x)
foo(3)
print(globals())
print('z = ',z)
globals()['z'] = 8 #globals()返回的是实际的全部名字空间,此时修改了变量z的值
print(globals())
print('z = ',z)
输出结果如下所示:
{‘x’: 1, ‘arg’: 3}
x = 1
{‘x’: 1, ‘arg’: 3}
x = 1
{‘name‘: ‘main‘, ‘doc‘: None, ‘package‘: None, ‘loader‘:
, ‘spec‘: None, ‘annotations‘: {}, ‘builtins‘: , ‘file‘: ‘D:/PythonWorkSpace/LocalsTest/LocalsTest2.py’, ‘z’: 7, ‘foo’: } z = 7
{‘name‘: ‘main‘, ‘doc‘: None, ‘package‘: None, ‘loader‘:
, ‘spec‘: None, ‘annotations‘: {}, ‘builtins‘: , ‘file‘: ‘D:/PythonWorkSpace/LocalsTest/LocalsTest2.py’, ‘z’: 8, ‘foo’: } z = 8
从输出结果可以看出对locals()修改,并不会对实际的局部名字空间中的值造成影响,而对globals返回的字典中的值进行修改,就直接影响到了全局变量的取值。