扩展Python搜索路径到其他源
我刚加入一个项目,这个项目的代码量挺大的。我们在Linux系统上开发,不使用集成开发环境(IDE),而是通过命令行来运行程序。我现在想弄明白,如何让Python在运行项目模块时找到正确的路径。比如,当我运行类似下面的代码时:
python someprojectfile.py
我得到的结果是:
ImportError: no module named core.'somemodule'
我在所有的导入中都遇到这个问题,所以我猜这可能是路径设置的问题。
我该如何让Python在导入文件时,搜索~/codez/project/
以及里面的所有文件和文件夹,找到*.py
文件呢?
7 个回答
我知道这个话题有点老了,但我花了一些时间才搞明白,所以想分享一下我的经验。
在我的项目中,我把主要的脚本放在一个父目录下,为了区分不同的模块,我把所有的辅助模块放在一个叫“modules”的子文件夹里。在我的主脚本中,我是这样导入这些模块的(以一个叫report.py的模块为例):
from modules.report import report, reportError
当我运行主脚本时,这样是可以正常工作的。不过,我想在每个模块里加一个main()
函数,然后直接调用它们,像这样:
python modules/report.py
结果Python就报错了,说找不到“modules”这个模块。这里的关键是,默认情况下,Python会把脚本所在的文件夹加入到搜索路径中,但不会把当前工作目录(CWD)加入。所以这个错误其实是在说“我找不到modules这个子文件夹”。这是因为在report.py模块所在的目录下并没有“modules”这个子目录。
我发现最简单的解决办法是在Python的搜索路径中加入当前工作目录,只需要在文件的开头加上这一行:
import sys
sys.path.append(".")
这样一来,Python就会搜索当前目录,找到“modules”这个子文件夹,一切就正常了。
你还可以在这里了解一下Python包的相关内容:http://docs.python.org/tutorial/modules.html。
根据你的例子,我猜你的包实际上是在~/codez/project
这个地方。Python目录中的__init__.py
文件可以把一个目录映射到一个命名空间。如果你的子目录里都有__init__.py
文件,那么你只需要把基础目录添加到你的PYTHONPATH
中。例如:
PYTHONPATH=$PYTHONPATH:$HOME/adaifotis/project
除了测试你的PYTHONPATH环境变量,正如David所解释的,你还可以在Python中这样测试:
$ python
>>> import project # should work if PYTHONPATH set
>>> import sys
>>> for line in sys.path: print line # print current python path
...
有几种方法可以做到这一点:
- 设置一个叫
PYTHONPATH
的环境变量,里面放上用冒号分隔的目录列表,这样 Python 就会在这些目录中查找你要导入的模块。 - 在你的程序里,可以用
sys.path.append('/path/to/search')
来添加你希望 Python 查找模块的目录名。sys.path
就是 Python 每次被要求导入模块时会查找的目录列表,你可以根据需要修改它(不过我不建议删除任何标准目录!)。你在PYTHONPATH
环境变量中放的任何目录,Python 启动时都会被加入到sys.path
中。 - 使用
site.addsitedir
来把一个目录添加到sys.path
。这个方法和直接添加的不同之处在于,使用addsitedir
时,它还会在这个目录中查找.pth
文件,并根据文件的内容可能会额外添加更多的目录到sys.path
。想了解更多细节可以查看文档。
你选择哪种方法取决于你的具体情况。记住,当你把项目分发给其他用户时,他们通常会以一种方式安装项目,使得 Python 的导入器能自动检测到 Python 代码文件(也就是说,包通常会安装在 site-packages
目录下),所以如果你在代码中修改 sys.path
,可能就没必要了,甚至在其他电脑上运行时可能会产生不好的效果。对于开发来说,我猜设置 PYTHONPATH
通常是最好的选择。
不过,当你使用的东西只是运行在你自己的电脑上(或者你有一些非标准的设置,比如在某些网络应用框架中),做一些类似的事情也是很常见的:
import sys
from os.path import dirname
sys.path.append(dirname(__file__))