本文主要探讨了python的import机制,会涉及到名称空间,变量作用域,import语句和相关的模块,以及包的管理等内容。
名称空间与作用域
名称空间: 就是名称和对象的绑定关系
作用域: 就是这个名称在哪些地方可见
我们可以这样理解一下,名称空间是说明这个名称"存在吗?",作用域是说明这个名称"我可以看见它吗?"。
名称空间一般有两个或者三个,为什么这么说呢,有两个是一只存在的,就是内建名称空间和全局名称空间,还有一个是局部名称空间,这个是在执行的时候,如果涉及到函数调用,那么会产生这个名称空间,如果函数调用结束,那么这个名称空间消失,所以说有两个或者三个名称空间。
名称空间的加载顺序是:内建名称空间,然后是加载执行模块的全局名称空间,最后如果有函数调用,出现局部名称空间。
名称查找的顺序是,先查找局部名称空间,然后查找全局名称空间,最后查找内建名称空间,如果都没有找到,那么会抛出NameError
的异常。这下就很好理解变量的覆盖了,为什么在函数里面如果有一个和外部同名的变量,使用的是这个函数内部的变量而不是外部的变量了,因为在查找局部名称空间的时候已经找到,那么就使用它,而不是在继续查找了。
locals和globals函数
首先它们两个都返回的是字典,key是名称,value是名称对应的值,顾名思义,locals
函数返回当前调用者的局部名称空间,globals
返回当前调用者的全局名称空间(也就是可以访问哪些全局的属性)。在全局名称空间下,它们返回的内容相同。
A = 1
def f(x):
y = 1
print(globals())
print(locals())
f(2)
返回的是:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000254BC468898>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/pycharm_wk/ETL/test.py', '__cached__': None, 'A': 1, 'f': }
{'y': 1, 'x': 2}
可以看见locals
返回了{'y': 1, 'x': 2}
,而globals
返回了很大一部分内容
我们直接在全局名称空间下使用:
print(globals())
print(locals())
返回的内容是:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000027CC0978898>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/pycharm_wk/ETL/test.py', '__cached__': None}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000027CC0978898>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/pycharm_wk/ETL/test.py', '__cached__': None}
其实是完全相同的内容了。
__name__
和__main__
我们知道__name__
指示的是名字,比如模块名,函数名,类名等,而__main__
只有在模块被直接执行的时候,__name__
就会等于__main__
。
import 和 load
模块在加载(load)的时候会被执行,也就是说所有处于模块顶层的代码都会被执行,导入(import)可以多次导入,但是模块只会被加载一次。
比如有三个py文件:
py1.py
print('py1')
py2.py
import py1
print('py2')
main.py
import py2
import py1
print('main')
当我们执行main.py的时候,打印结果是:
py1
py2
main
- 我们在
main.py
里面import py2
,首先加载并且执行py2.py
, - 但是
py2.py
又引入了import py1
,所以加载并且执行py1.py
,所以先打印了py1
, -
py1.py
加载完成后,回到py2.py
,打印py2
,执行完成后回到了main.py
, - 遇到了
import py1
,python判断py1
已经被import
,不会重复import
,所以执行下面的打印main
。
总结:模块的顶层代码(简单理解,就是没有缩进的代码)在
import
的时候会被执行,但是多次import
只会执行一次。
zip方式的导入
python支持导入zip方式,python会把zip文件看成一个目录(也就是一个包),和正常的使用方式一样。
pyc文件
通过python -m py_compile dst.py
可以把dst.py
文件编译为对应的pyc
文件,当我们在使用import
第一次导入某一个模块的时候,其实python会先load,其实也就是执行了导入的模块,生成了pyc文件,导入的也是这个里面的内容,那么我们完全可以提供pyc文件,而不用提供py源文件,别人的引入也是有效的。
reload函数
python3已经没有reload
内建函数。可以使用imp和import模块实现同样的功能,而且提供了更多有用的函数。