一.模块, 包

模块: 本质上python中每个文件都是一个模块 包: 除了包含模块外,还包含__init__.py文件,同时它允许嵌套。

定义一个包,只需要在当前目录下包含__init__.py文件。 1.__init__.py可以为空 2.在__init__.py定义__all__指定的是指此包被import * 的时候, 哪些模块会被import进来 包组织结构形如

 package/__init__.py
    module1.py
    module2.py
    subpackage/__init__.py
       module3.py

二.导入,加载模块

python在初始化运行环境的时候会预先加载一批内建模块到内存中,这些模块的信息被存放在sys.modules中。读者导入sys模块后在python解释器中输入sys.modules.items() 便可显示所有预加载模块的相关信息。当加载一个模块的时候,解释器实际上要完成以下动作:

1.在sys.modules中进行搜索看看该模块是否已经存在,如果存在,则将其导入当前局部命名空间,
加载结束
2.如果在sys.modules中找不到对应模块的名称,则为需要导入的模块创建一个字典对象,
并将该对象信息插入到sys.modules中
3.加载前确认是否需要对模块对应的文件进行编译,如果需要则先进行编译
4.执行动态加载,在当前模块的命名空间中执行编译后的字节码,并将其中所有的对象放入模块所对应的字典中

执行到第三步时, 涉及到如何查找模块.

三.查找模块

 在一个模块被导入时,PVM会在后台从一系列路径中搜索该模块,其搜索过程如下:
 1、在当前目录下搜索该模块;
 2、在环境变量PYTHONPATH中指定的路径列表中依次搜索;
 3、在python安装路径中搜索

事实上,PVM(Python虚拟机)通过变量sys.path中包含的路径来搜索,这个变量里面包含的路径列表就是上面提到的这些路径信息,我们可以打印看下sys.pth都包含些哪些路径:

1
2
3
 >>import sys
 >>print sys.path
['', '/Library/Python/2.7/site-packages/pip-8.0.3-py2.7.egg', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC', '/Library/Python/2.7/site-packages']

任何的import都会按照sys.path来查找目标模块, 当然我们也可以自行在运行时追加新的模块路径到sys.path中

四.用法比较

引入外部模块的三种方法, import语句,from … import …, __import__函数 from … import *

   __import__函数与import语句类似, 不同点在于前者显式地将模块的名称作为字符串传递并赋值给命名空间的变量

在使用import时,注意以下几点:

  1.一般情况下,尽量优先使用import a形式,如访问B时,需要使用a.B的形式
  2.有节制地使用from a import B形式,可以直接访问B
  3.尽量避免使用from a import *, 因为这会污染命名空间,并且无法清晰地表示导入了哪些对象

以用户自定义的模块为例来看看sys.modules和当前局部命名空间的变化

创建一个简单的模块testmoudle.py

1
2
3
  a = 1
  b = 'a'
  print 'testing module import'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>dir()
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
>>import testmoudle
testing module testmodule
>>dir()  #import testmodule之后局部命名空间发生变化
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'testmodule']
>>'testmodule' in sys.modules.keys()
True
>>id(testmodule) == id(sys.modules['testmodule'])
True
>>dir(testmodule)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'b']
>>sys.modules['testmodule'].__dict__.keys()
['a', 'b', '__builtins__', '__file__', '__package__', '__name__', '__doc__']

从输出结果看, 对于用户自定义的模块,import机制会创建一个新的module将其加入当前的局部命名空间 中,与此同时,sys.modules也加入该模块的相关信息,从它们的id输出来看,本质是引用同一对象。同时发现testmodule所在目录下多了一个.pyc文件,该文件为解释器生成的模块相对应的字节码文件。从 ‘testing module import’可以看出模块同时被执行, 而a,b被写入到了testmodule所对应的字典信息中。

import a 和from a import B的差异:后者直接将B暴露与当前局部空间,而前者将a加载到sys.modules集合.
from...import *也是直接将B暴露与当前局部空间
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>import sys
>>dir()
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'sys']
>>from testmodule import a  #a暴露到了当前局部空间
testing module import
>>dir()
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'sys']
>>id(a)
140327233018008
>>id(sys.modules['a'])
Traceback (most recent call last):
  File "mod.py", line 8, in <module>
    print id(sys.modules['a'])
KeyError: 'a'

使用from…import过于频繁,容易导致命名空间冲突 比如 文件a.py如下:

1
2
def add():
    print 'add in module A'

文件b.py如下:

1
2
def add():
    print 'add in module B

文件importtest.py如下:

1
2
3
4
5
from a import add
from b import add

if __name__ == '__main__':
    add()

输出:

1
add in module B

可以看出,实际起作用的是最近导入的add(),完全覆盖了之前从a导入的add() 所以应该有节制的使用from…import

from…import…适用场景:

1.当只需要导入部分属性和方法时
2.模块中的这些属性和方法访问频率较高导致 ‘模块名.名称’进行访问过于繁琐
3.模块的文档说明需要使用from...import形式,导入的是一个包下面的子模块,
且使用from...import形式更为简单,如使用from io.drivers import zip比使用
import io.drivers.zip要方便

__import__函数

内置函数__import__()以字符串为参数导入模块,导入的模块会被添加到sys.modules,但不会在当前
命名空间中创建引用
1
2
3
4
5
6
7
8
>>dir()
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'sys']
>>__import__('testmodule')
testing module import
>>dir()
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'sys']
>>sys.modules.get('testmodule')
<module 'testmodule' from '/Users/tmackan/script/Python/fluent/unit3/testmodule.pyc'>

Final:《Effective Python》读书总结