Chinaunix首页 | 论坛 | 博客
  • 博客访问: 232210
  • 博文数量: 96
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-14 11:43
文章分类

全部博文(96)

文章存档

2016年(41)

2015年(55)

我的朋友

分类: Python/Ruby

2016-01-13 14:09:14

模块基本概念以及搜索路径


在python用import或者from...import来导入相应的模块。模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中,我们就可以使用了。

python中的Module是比较重要的概念。常见的情况是,事先写好一个.py 件,在另一个文件中需要import时,将事先写好的.py文件拷贝 到当前目录,或者是在中增加事先写好的.py文件所在的目录,然后import。这样的做法,对于少数文件是可行的,但如果程序数目很 多,层级很复杂,就很吃力了。
有没有办法,像JavaPackage一样,将多个.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) |
给主人留下些什么吧!~~