使用sys.path.insert(0, path)和sys.path.append(path)加载模块时的影响

87
我最近遇到了一个Python ImportError的问题,当我在本地计算机上运行时,模块是可以找到的,但在CI服务器上找不到。我通过将脚本中的sys.path.append(path)替换为sys.path.insert(0, path)来解决了这个问题,其中path是模块位置的字符串。
由于这是我的模块而不是已安装的包(相关问题),为什么路径的顺序可以解决这个问题?

2个回答

86

我是Python的初学者,我发现Anand的回答非常好但对我来说有些复杂,所以我试图重新表述:

1)insertappend方法并不专属于sys.path,它们像其他语言一样向列表或数组中添加一个项目:
* append(item)item添加到列表末尾,
* insert(n, item)在列表中的第n个位置插入item0表示在开始处插入,1表示在第一个元素后面插入等等)。

2)正如Anand所说,Python按照路径的顺序在每个目录中搜索导入的文件,因此:
* 如果没有文件名冲突,则路径的顺序没有影响,
* 如果你正在寻找已在路径中定义的函数并使用append添加路径,则不会得到你的函数而是预定义的函数。

但我认为最好使用append而不是insert,以避免过多地重载Python的标准行为,并为你的文件和方法使用非歧义的名称。


59
因为Python按顺序检查目录,从sys.path列表中的第一个目录开始,直到找到它正在寻找的.py文件为止。
通常情况下,当前目录或脚本所在的目录是列表中的第一个元素,除非你进行了修改(就像你所做的那样)。来自documentation的说明如下:

在程序启动时初始化,列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果以交互方式调用解释器或者从标准输入读取脚本),path[0]是空字符串,这将指示Python首先在当前目录中搜索模块。请注意,脚本目录在PYTHONPATH的结果之前插入。

所以,很可能在当前目录(脚本运行的位置)中有一个与你尝试从中导入的模块同名的.py文件。
此外,关于ImportError的一点需要注意的是 - 假设导入错误显示为:ImportError: No module named main。这并不意味着main.py被覆盖了 - 如果被覆盖了,它仍然存在,我们在尝试读取它时不会出现问题。而是在它上面的某个模块被一个.py或其他文件覆盖了。
例如,考虑以下目录结构:
 - test
    - shared
       - __init__.py
       - phtest.py
    - testmain.py

现在从testmain.py中,我调用from shared import phtest,这个运行得很好。
现在假设我在test目录中引入了一个shared.py文件。
 - test
    - shared
       - __init__.py
       - phtest.py
    - testmain.py 
    - shared.py

现在当我尝试从testmain.py中执行from shared import phtest时,我收到以下错误信息:
ImportError: cannot import name 'phtest'

如您所见,引发问题的文件是shared.py,而不是phtest.py

1
我的模块名是something.main,我尝试将其更改为something.engine以查看是否存在问题,但我仍然遇到了ImportError错误,这让我认为这不是模块名称或文件冲突所导致的。 - Michael Barton
2
回溯信息的最后一行是“ImportError: No module named main”。 - Michael Barton
1
你有追踪信息吗?或许那能帮到你。 - Anand S Kumar
1
这是CircleCI的输出,恐怕有点有限 - https://circleci.com/gh/bioboxes/command-line-interface/18 - Michael Barton
1
问题很可能是由当前目录(或PYTHONPATH变量中的某个目录)中的biobox_cli.py引起的,请检查这两个地方。 - Anand S Kumar
显示剩余2条评论

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