从不同的工作目录导入Python模块

31

我有一个使用内置模块和一些自定义模块的Python脚本,这些自定义模块存在于与主脚本相同的目录中。

例如,我会调用:

python agent.py

而且agent.py文件有很多导入,其中包括:

import checks

checks文件与agent.py在同一目录中。

agent/agent.py
agent/checks.py

如果当前的工作目录是agent/,那么一切都很顺利。然而,如果我从任何其他目录调用agent.py,显然无法导入checks.py,因此会出现错误。

我该如何确保自定义模块可以在agent.py被从任何位置调用时都能被导入,例如:

python /home/bob/scripts/agent/agent.py

结果是什么?你打算接受答案吗? - John Machin
我会在尝试代码后接受答案,但这需要几周时间。我并不觉得有什么特别的紧迫性。 - davidmytton
你尝试了什么代码?正如我已经试图通过一个例子指出的那样,还有一两个人也提到过,你所说的应该可以直接运行,并且已经有文档说明。与其“尝试代码”,不如按照我的建议告诉我们基本信息(平台、Python版本、完整的回溯和错误消息),并执行我建议的基本调试...只需要几分钟时间;六个人“匆忙”给你答案。 - John Machin
7个回答

31

事实上,你的示例有效是因为checks.py与agent.py在同一个目录中,但是假设checks.py位于前面的目录中,例如;

agent/agent.py
checks.py

那么你可以按照以下步骤进行:

path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not path in sys.path:
    sys.path.insert(1, path)
del path

注意使用__file__


关于del path有什么需要注意的吗?为什么它很特别? - Charlie Parker
@CharlieParker 这是为了清理 path 变量而设置的。如果你想让它留在那里,也没问题。 - Mr. S
3
对于像我这样的Python新手,希望您能加上“import os,sys”(不带引号),这样代码才能正常运行 :) - icasimpan
第二个片段应该放在agent.py还是checks.py中? - Chang

12

你不应该需要操作sys.path。引用Python 2.6文档中对sys.path的说明:

 

在程序启动时,初始化这个列表的第一项path[0]是包含用来调用Python解释器的脚本的目录。如果脚本目录不可用(例如如果解释器是交互式调用或者脚本是从标准输入读取的),那么path[0]为空字符串,它会告诉Python首先在当前目录中搜索模块。请注意,脚本目录被插入到PYTHONPATH导致的条目之前。

=== amod.py ===
def whoami():
    return __file__

=== ascript.py ===
import sys
print "sys.argv", sys.argv
print "sys.path", sys.path
import amod
print "amod __file__", amod.whoami()

=== result of running ascript.py from afar ===
C:\somewhere_else>\python26\python \junk\timport\ascript.py
sys.argv ['\\junk\\timport\\ascript.py']
sys.path ['C:\\junk\\timport', 'C:\\WINDOWS\\system32\\python26.zip', SNIP]
amod __file__ C:\junk\timport\amod.py

如果重新运行,最后一行将会改变成...amod.pyc。这种情况似乎并不新奇,它适用于Python 2.1和1.5.2版本。

以下是您的调试提示:请尝试两个简单的文件,就像我所做的那样。尝试使用 -v 和 -vv 选项运行 Python。展示包括完整回溯和错误消息以及两个文件在内的失败测试结果。告诉我们您正在运行的平台和 Python 版本。检查 checks.py 文件的权限。是否存在一个名为 checks.something_else 的文件干扰了您的测试?


我觉得这应该是正确的答案... OP 明显谈论的是调用脚本而不是从不同位置导入脚本。 - Guru Prasad

7

你需要将当前执行模块的路径添加到sys.path变量中。由于你在命令行上调用它,脚本的路径始终在sys.argv[0]中。

import sys
import os
sys.path.append(os.path.split(sys.argv[0])[0])

现在当导入搜索模块时,它也会查找托管agent.py文件的文件夹。

5

有几种方法可以将内容添加到 PYTHONPATH 中。

请阅读 http://docs.python.org/library/site.html

  1. 在运行脚本之前设置 PYTHONPATH 环境变量。

    您可以使用 python -m agent 来从您的 PYTHONPATH 运行 agent.py

  2. 在您的 lib/site-packages 目录中创建 .pth 文件。

  3. 将模块安装在 lib/site-packages 中。


1
为了概括你的目标,你想要能够在任何地方调用Python并且无论你的Python脚本位于哪里,都可以使用import custom_package_name导入自定义包。
许多答案提到了我将要描述的内容,但我觉得大部分答案假设了很多先前的知识。我会尽可能明确地说明。
为了实现允许通过import语句导入自定义包的目标,它们必须在Python用于搜索包的路径中的某个位置可被发现。 Python实际上使用多个路径,但我们只关注可以通过将sys.prefix(在您的Python解释器中)的输出与/lib/pythonX.Y/site-packages(如果您使用Windows,则为lib/site-packages)结合起来找到的路径,其中X.Y是您的Python版本。
具体来说,通过运行以下命令找到Python使用的路径:
import sys
your_path = sys.prefix + '/lib/pythonX.Y/site-packages'
print(your_path)

如果你正在使用Python 3.5,则此路径应该类似于/usr/local/lib/python3.5/site-packages,但根据你的设置情况可能会有很大不同。

Python使用此路径(和其他几个路径)来查找要导入的包。因此,你可以将自定义的包放在/usr/local/lib/python3.5/site-packages文件夹中。别忘了向文件夹中添加一个init.py文件。

具体来说,在终端中键入:

cd your_path
cp path_to_custom_package/custom_package ./

现在,您应该能够像导入位于同一目录中的包一样导入自定义包中的所有内容(即,对于包中的每个子包文件,import package.subpackage 应该起作用)。

一些问题是永恒的。 - saetch_g

1

我认为你应该考虑将代理目录制作成一个合适的Python包。然后,你可以将这个包放在Python路径的任何位置,并且可以导入检查。

from agent import checks

请查看http://docs.python.org/tutorial/modules.html


1

如果您知道要检查的 check.py 的完整路径,请使用此配方(http://code.activestate.com/recipes/159571/

如果您想将目录添加到系统路径中 - 请使用此配方(http://code.activestate.com/recipes/52662/)。在这种情况下,我必须确定应用程序目录(sys.argv[0])并将该值传递给 AddSysPath 函数。如果您想查看生产样本,请在此线程上留言,以便我稍后发布。

问候。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接