理解包

你首先需要理解的是Python没有一个默认的包管理设施。事实上,包的概念在Python中是相当弱的。

可能你已经知道,Python代码被组织为模块。
一个模块可能由包含一个函数的单一文件组成,也可能由包含多个模块的目录组成。
包和模块的区别非常小,并且每个模块都能被理解为包。

那么包和模块的区别到底是什么(如果有的话)?为了明白这个,你首先应该明白Python是如何查找模块的。

如同别的编程环境一样,Python中的一些函数和类(例如str,len,Exception等)在全局(叫做内置函数)都是可用的。
别的就需要通过手动 import 进来。例如:

  1. >>> import os
  2. >>> from os.path import basename, dirname

这个包一定存在你的机子上,这样才能被import语句导入。但Python是如何知道这些模块的位置呢?
这些位置信息在你安装Python虚拟机时就被自动设置好了,并且依赖于你的目标平台。

包的路径可以在sys.path中查询。下面是在我的笔记本上的结果,运行环境是Ubuntu 11.10。

  1. >>> import sys
  2.  
  3. >>> print sys.path
  4.  
  5. ['',
  6.  
  7. '/usr/lib/python2.7',
  8.  
  9. '/usr/lib/python2.7/plat-linux2',
  10.  
  11. '/usr/lib/python2.7/lib-tk',
  12.  
  13. '/usr/lib/python2.7/lib-old',
  14.  
  15. '/usr/lib/python2.7/lib-dynload',
  16.  
  17. '/usr/local/lib/python2.7/dist-packages',
  18.  
  19. '/usr/lib/python2.7/dist-packages',
  20.  
  21. '/usr/lib/python2.7/dist-packages/PIL',
  22.  
  23. '/usr/lib/python2.7/dist-packages/gst-0.10',
  24.  
  25. '/usr/lib/python2.7/dist-packages/gtk-2.0',
  26.  
  27. '/usr/lib/pymodules/python2.7',
  28.  
  29. '/usr/lib/python2.7/dist-packages/ubuntu-sso-client',
  30.  
  31. '/usr/lib/python2.7/dist-packages/ubuntuone-client',
  32.  
  33. '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel',
  34.  
  35. '/usr/lib/python2.7/dist-packages/ubuntuone-couch',
  36.  
  37. '/usr/lib/python2.7/dist-packages/ubuntuone-installer',
  38.  
  39. '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']

这里给出了Python搜索包的路径。它将从最上面开始找,直到找到一个名字相符的。
这表明如果两个不同的路径分别包含了两个具有相同名字的包,搜索将在找到第一个名字的时候停止,然后将永远不会往下查找。

正如你所猜的,包搜索路径很容易被劫持,为了确保Python首先载入你的包,所需做的如下:

  1. >>> sys.path.insert(0, '/path/to/my/packages')

尽管这个方法在很多情况下都很好用,但一定要小心不要滥用。 只有当必要时再使用!不要滥用!

site 模块控制包的搜索路径。当Python虚拟机初始化时它会自动被导入。如果你想了解更多信息,请看 官方文档

PYTHONPATH变量

PYTHONPATH 是一个用来增加默认包搜索目录的环境变量。可以认为它是对于Python的一个特殊的 PATH 变量。
它仅仅是一个通过 : 分割,包含Python模块目录的列表(并不是类似于 sys.path 的Python list)。
它可能就类似下面这样:

  1. export PYTHONPATH=/path/to/some/directory:/path/to/another/directory:/path/to/yet/another/directory

有时候你可能并不想覆盖掉现存的 PYTHONPATH ,而仅仅是希望添加新目录到头部或尾部。

  1. export PYTHONPATH=$PYTHONPATH:/path/to/some/directory # Append
  2. export PYTHONPATH=/path/to/some/directory:$PYTHONPATH # Prepend

PYTHONPATHsys.path.insert 这些方法并非完美,我们最好也不要用这些方法。
使用它们,你可能可以解决本地的开发环境问题,但它在别的环境下也许并不适用。
有很多种方法可以达到这个效果,在下面我将一一阐述。

我们现在明白的Python如何找到安装的包路径,现在让我们回到开始那个问题。
模块和包的区别到底是什么?包是一个模块或模块/子模块的集合,一般情况下被压缩到一个压缩包中。
其中包含1)依赖信息 2)将文件拷贝到标准的包搜索路径的指令。3)编译指令(如果在安装前代码必须被编译的话)。
就这些东西!