【问题标题】:Different import behaviour in identical code in interactive vs. non-interactive mode - why does the module search path differ?交互与非交互模式下相同代码中的不同导入行为 - 为什么模块搜索路径不同?
【发布时间】:2020-02-23 20:18:21
【问题描述】:

这个问题与Can't import module with importlib.import_module有关,我认为这根本不是问题的根源。

我试图了解交互式解释器与非交互式脚本中使用的importlib 存在差异的原因,因为我得到的结果不同。

我有一个包含myscript.py 的文件夹,我希望它从初始工作目录的子文件夹my_subdir 中导入一个名为module 的模块。我想通过进入该目录并在那里工作来做到这一点。我知道访问子目录中的模块还有其他解决方案,但这是我想谈谈的用例。

以下是myscript.py的内容,它只是移动到子目录并尝试从那里导入模块,然后从模块中调用一些hello-world函数:

#!/home/chris/anaconda2/bin/python
import sys
import platform
import os
import importlib

print("Python version "+ platform.python_version())
print(sys.path)

os.chdir('my_subdir')
my_module = importlib.import_module('module')
my_module.my_function()

当我从命令行运行它时,Python 找不到该模块。好像导入系统不知道os.chdir()的目录变化:

(base) chris@linux-om3m:~/workspace> python myscript.py 
Python version 2.7.15
['/home/chris/workspace', '/home/chris/anaconda2/lib/python27.zip', '/home/chris/anaconda2/lib/python2.7', '/home/chris/anaconda2/lib/python2.7/plat-linux2', '/home/chris/anaconda2/lib/python2.7/lib-tk', '/home/chris/anaconda2/lib/python2.7/lib-old', '/home/chris/anaconda2/lib/python2.7/lib-dynload', '/home/chris/anaconda2/lib/python2.7/site-packages', '/home/chris/anaconda2/lib/python2.7/site-packages']
Traceback (most recent call last):
  File "myscript.py", line 11, in <module>
    my_module = importlib.import_module('module')
  File "/home/chris/anaconda2/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
ImportError: No module named module

但是,当我在交互式解释器中键入相同的脚本时,它可以正常工作:

(base) chris@linux-om3m:~/workspace> python
Python 2.7.15 | packaged by conda-forge | (default, Jul  2 2019, 00:39:44) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import platform
>>> import os
>>> import importlib
>>> print("Python version "+ platform.python_version())
Python version 2.7.15
>>> print(sys.path)
['', '/home/chris/anaconda2/lib/python27.zip', '/home/chris/anaconda2/lib/python2.7', '/home/chris/anaconda2/lib/python2.7/plat-linux2', '/home/chris/anaconda2/lib/python2.7/lib-tk', '/home/chris/anaconda2/lib/python2.7/lib-old', '/home/chris/anaconda2/lib/python2.7/lib-dynload', '/home/chris/anaconda2/lib/python2.7/site-packages', '/home/chris/anaconda2/lib/python2.7/site-packages']
>>> os.chdir('my_subdir')
>>> my_module = importlib.import_module('module')
>>> my_module.my_function()
Here I am!
>>> 

我可以通过在执行os.chdir 后手动更新sys.pathsys.path.append(os.getcwd()) 来解决此问题 - 但我不明白为什么这仅在非交互模式下才有必要。

我唯一注意到(并在此处粘贴的输出中打印)是在交互式解释器下,sys.path 有一个空字符串条目,而在非交互模式下,它包含我调用的工作目录蟒蛇。

我知道我可以使用相对导入来解决这个问题,现在我正在使用sys.path.append(os.getcwd()) 来解决这个问题,但我想了解为什么交互式运行与非交互式运行之间存在差异。

我在 Python 2.7 和 Python 3.6 中得到了相同的结果。

【问题讨论】:

  • 其实我也可以在非交互模式下通过sys.path.append('')修复它,所以我猜''有特殊含义。但问题仍然是为什么交互式解释器在sys.path 上有空字符串,而非交互式解释器没有(因此我们在两者之间有完全不同的行为!)。

标签: python python-3.x linux python-2.7


【解决方案1】:

正如您在评论中指出的那样,将 '' 附加到 sys.path 可以解决此问题。这是因为'' 代表你的 cwd,所以os.chdir() 知道在哪里可以正确找到你的子目录my_subdir(参见Why is the first element in python's sys.path an empty string?)。

使用my_subdir 的绝对路径也应该可以解决您的问题。

【讨论】:

  • 这是有道理的——但是为什么这两种情况的行为不一样呢?为什么非交互版本也不使用''
  • 啊好的docs.python.org/3/library/sys.html#sys.path - 如果脚本目录不可用,则使用'' - 例如在交互模式下。这是有道理的,但最终这会导致意想不到的(而且令人惊讶!)不同的行为。
猜你喜欢
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2016-05-09
  • 1970-01-01
相关资源
最近更新 更多