模块基本概念以及搜索路径
在python用import或者from...import来导入相应的模块。模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中,我们就可以使用了。
python中的Module是比较重要的概念。常见的情况是,事先写好一个.py文 件,在另一个文件中需要import时,将事先写好的.py文件拷贝 到当前目录,或者是在中增加事先写好的.py文件所在的目录,然后import。这样的做法,对于少数文件是可行的,但如果程序数目很 多,层级很复杂,就很吃力了。
有没有办法,像Java的Package一样,将多个.py文件组织起来,以便在外部统一调用,和在内部互相调用呢?答案是有的。
主要是用到python的包的概念,python
__init__.py在包里起一个比较重要的作用
要弄明白这个问题,首先要知道,python在执行import语句时,到底进行了什么操作,按照python的文档,它执行了如下操作:
第1步,创建一个新的,空的module对象(它可能包含多个module);
第2步,把这个module对象插入sys.module中
第3步,装载module的代码(如果需要,首先必须编译)
第4步,执行新的module中对应的代码。
在执行第3步时,首先要找到module程序所在的位置,其原理为:
如 果需要导入的module的名字是m1,则解释器必须找到m1.py,它首先在当前目录查找,然后是在环境变量PYTHONPATH中查找。 PYTHONPATH可以视为系统的PATH变量一类的东西,其中包含若干个目录。如果PYTHONPATH没有设定,或者找不到m1.py,则继续搜索 与python的安装设置相关的默认路径,在Unix下,通常是/usr/local/lib/python。
事实上,搜索的顺序是:当前路径 (以及从当前目录指定的sys.path),然后是PYTHONPATH,然后是python的安装设置相关的默认路径。正因为存在这样的顺序,如果当前 路径或PYTHONPATH中存在与标准module同样的module,则会覆盖标准module。也就是说,如果当前目录下存在xml.py,那么执 行import xml时,导入的是当前目录下的module,而不是系统标准的xml。
了解了这些,我们就可以先构建一个package,以普通module的方式导入,就可以直接访问此package中的各个module了。
Python中的package定义很简单,其层次结构与程序所在目录的层次结构相同,这一点与Java类似,唯一不同的地方在于,python中的package必须包含一个__init__.py的文件。
例如,我们可以这样组织一个package:
package1/
__init__.py
subPack1/
__init__.py
module_11.py
module_12.py
module_13.py
subPack2/
__init__.py
module_21.py
module_22.py
……
__init__.py可以为空,只要它存在,就表明此目录应被作为一个package处理。当然,__init__.py中也可以设置相应的内容,下文详细介绍。
标准 Import
Python中所有加载到内存的模块都放在sys.modules。当import一个模块时首先会在这个
列表中查找是否已经加载了此模块,如果加 载了则只是将模块的名字参加
到正在调用import的模块的Local名字空间中。如果没有加载则从sys.path目录中遵守模块名称查找模块文件,模
块文件可以是py、pyc、pyd,找到后将模块载入内存,并参加 到sys.modules中,并将名称导入到当前的Local名字空间。
可以看出了,一个模块不会重复 载入 。多个不同的模块都可以用import引入同一个模块到自己的Local名字 空间,其实背后的PyModuleObject对象只有一个。
说一个容易漠视 的问题,import只能导入模块,不能导入模块中的对象(类、函数、变量等)。 如一个模块 A(A.py)中有个函数getName,另一个模块不能通过import A.getName将getName导入到本模块,只能用import A。如果想只导入特定的类、函数、变量则用from A import getName即可。
嵌套Import
嵌套import,我分两种情况 :
顺序嵌套:本模块导入A模块(import A),而A中又有import语句,会激活另一个import动作,如import B,而B模块又可以import其他模块,一直下去。
对这种嵌套对比 容易了解,注意一点就是各个模块的Local名字空间是独立的,所以上面的例子,本模块import A完了后本模块只能造访 模块A,不能造访 B及其他模块。虽然模块B已经加载到内存了,如果要造访 还要在明确 的在本模块中import B。
循环嵌套:【业务应用场景有限,暂不深究】
模块初始化
模块会在导入的时候自动执行可执行语句。结果都会保存在模块自己的名字空间之下,访问时,需要加前缀 name.
脚本初加载时,python会顺序执行所有可以执行的东西。 如果是函数与类定义,它就顺序将定义放到全局表里。
这里全局变最也是一样。python从import语句开始执行,执行到这句话today 就被初始化了。
如果这个模块被其它模块import ,那个在import 的时候, today会被加载。
不过跨模块的全局变量,在python里有些古怪。要小心使用。
跨模块全局变量
昨天遇到一个诡异的问题
多个.py文件去操作访问一个全局变量的时候,有个py文件访问到的是空值,昨天折腾了一晚上没搞定,上午突然想到是否是调用了2次定义全局变量module导致
所以调试了下,发现:
1. 定义该全局变量的module确实被import了2次,而且是当做不同的sys.module的key
2. 于是第二次 import的时候当做另外一个全局变量了
这个问题的原因是:
1. python import 包的机制是,import进来的和默认的系统的module了,都放在sys.module这个字典里面
2. 多个py文件再次import的时候,会先去sys.module里面检查是否已经import了,如果已经import了,就不再重复import,否则就import进来
3. 问题的关键是,如果a.py 定义如下:
import abc
如果直接从a目录执行,sys.module里面有个key叫abc
b.py 定义如下import abc
如果 b.py被调用的地方是采用包结构,比如.
from ./../**/ import b
这个时候, sys.module里面的key则是 ../../**/b
这样,就重新加载了
解决该问题的办法是,尽量都用包结构去import,这样能保证import只有一次
阅读(1287) | 评论(0) | 转发(0) |