Python导入、路径、目录和模块

6

首先,我要说我在过去的一周里进行了广泛的研究,但仍没有找到这些问题的实际答案 - 只有一些模糊的答案,不能真正解释发生了什么。如果这只是因为我错过了我正在寻找的东西,我很抱歉 - 请直接指出正确的方向。

我的目录结构如下:

TestProject/
    runtest*
    testpackage/
        __init__.py
        testmod.py
        testmod2.py
        testsubs/
            testsubmod.py

一些注意事项:
- 我使用的是Ubuntu上的python2.7 - 我正在使用bpython进行测试 - 我从特定目录运行bpython以测试导入的方式 - 我试图遵循最佳实践。 - 此软件包未安装,而是在随机的开发目录中 - 此目录不在pythonpath中 - 我在软件包目录中只有一个init.py - 嵌套目录中没有init.py文件 - init.py文件为空 - testpackage/testmod.py包含TestModClass - testpackage/testsubs/testsubmod.py包含TestSubModClass
我观察到的事情:
- 当我从TestProject/运行bpython时,import testpackage可以工作 - 这不会导入testpackage.testmod - 我无法访问testpackage.testmod - 当我从TestProject/运行bpython时,import testpackage.testmod失败 - 当我从TestProject/运行bpython时,from testpackage import testmod可以工作 - 我可以向init.py添加代码以显式导入testmod.py,但不能导入testsubs/testmod.py - 我认为这不是正确的方法,如果用户不想导入该模块怎么办?
- 从testmod.py中,我可以导入testmod2,但无法导入testpackage.testmod2 - 这很好做,这样我就可以用与STL或twisted重叠的名称(例如testpackage.logging)命名自己的模块,而不会导致错误(必须将自己的模块命名为customerlogging,而不仅仅是mypackage.logging)
问题:
- Python在处理存在于pythonpath中的包和模块时是否与从当前目录导入有所不同? - 为什么import testpackage不能让我访问testpackage.testmod?当我导入os时,我可以访问os.path等。 - 对于一个包,我应该坚持在基本目录中使用单个init.py,还是应该将它们嵌套在后续目录中? - 如何导入指定包名称的模块?例如,从testmod.py中,我想导入testpackage.testmod2而不仅仅是testmod2。 - 导入子模块的正确方法是什么?
- 我看到的唯一解决方案是从init.py将该目录附加到pythonpath中,但我不知道这是否是正确的方法。
提前感谢您。

  1. testsubs也需要一个__init__.py,它是一个子包。
  2. 因为os的__init__.py这样说。
  3. 包需要在您的路径中。
  4. 没有子模块,只有子包。
- agf
2个回答

9
首先,在Python教程的第6节中你可以找到所有需要的信息

(1) Python在导入模块时对PythonPath中存在的包和模块是否有不同的处理方法?

不,没有。实际上,Python在导入模块时总是搜索sys.path。只有因为空字符串的条目(代表当前目录)包含在sys.path中,才能找到当前目录中的模块。


(2) 为什么import testpackage不能让我访问testpackage.testmod? 而当我导入os后,我就可以访问os.path等。

出于效率的考虑,import testpackage只会加载testpackage/__init__.py。如果需要testpackage.testmod,必须显式地导入它:

import testpackage   # Just imports testpackage, not testpackage.testmod!
import testpackage.testmod   # Import *both* testpackage and testpackage.testmod!

如果您想要始终导出testmod,请在__init__.py中导入它,这就是os (os/__init__.py)所做的。这样,如果您导入testpackagetestpackage.testmod将始终隐式可用。
由于Python跨平台,实际上没有一种方法可以一致且自动地加载目录中的模块,因为某些文件系统不区分大小写(Windows!)。 Python不知道是否将os/path.py加载为os.pathos.Path等。
(3) 对于包,我应该坚持在基本目录中使用单个__init__.py,还是应该将其嵌套在后续目录中?
您总是需要为每个子包使用一个__init__.py。虽然曾经讨论过放弃此要求,但决定保留它。
(4) 如何导入指定包名称的模块?例如,从testmod.py,我想要导入testpackage.testmod2而不仅仅是testmod2
这应该有效。只需确保从顶级目录运行代码即可。如果当前目录是testpackage,则testmod不知道它在一个包中。
更好的方法是使用相对于包内的导入。
from . import testmod2

这样做可以防止名称冲突,如果有一个名为testmod2的全局模块,它使您能够在包内使用知名模块的名称而不会出现问题。

(5)如何正确地从subsubs目录导入子模块? 我唯一看到的解决方案是从__init__.py附加该目录到pythonpath中,但我不知道是否正确。

不,不要那样做!当父目录已经在sys.path中时,绝对不要将目录放到sys.path中!这可能会导致模块加载两次,这是不好的!

通常,您应该能够使用绝对或相对导入来从子包中加载模块:

import testpackage.testsubs.testsubmod
from testpackage.testsubs import testsubmod
from .testsubs import testsubmod

请确保在testsubs/中创建一个__init__.py文件!


啊哈!这回答了很多问题,但最重要的是 - 是否可以从包中导入模块取决于包的__init__.py文件是否导入了这些模块!我修改了根__init__.py以导入testmod.py但不导入testmod2.py - 现在,从TestProject目录中我可以导入testpackage.testmod但不能导入testpackage.testmod2.py。有趣! - nfarrar
@nathanfarrar - 不,你误解了。如果有一个 package/__init__.py 和一个 package/foo.py, 你总是可以使用 import package.foo 来引用它们,无论 __init__.py 是否引用了 foo!但是,你必须 import package.foo,单独的 import package 不会导入 foo - Ferdinand Beyer
啊对,我现在看明白了。谢谢! - nfarrar
1
我无法让相对导入正常工作 - 从. 导入testmod2导致ValueError:尝试在非包中进行相对导入,但该目录包含__init__.py文件。 - nfarrar
实际上,如果我导入包,它可以工作,但是如果我尝试直接执行包含“from .import submod2”的testmod.py文件,则会失败。 - nfarrar
当您尝试此操作时,当前的工作目录是什么?正如我所说,您必须从顶级目录运行Python。如果您想要运行一个包模块,请使用python -m testpackage.testmod,而不是python testpackage/testmod.py - Ferdinand Beyer

0
  1. 不是的。当前目录只是添加到PYTHONPATH中。
  2. 首先,你需要在其中一个文件夹中有一个__init__.py文件。其次,os.path是因为os正在导入os.path。
  3. 你需要在每个目录中都有一个__init__.py文件,包括包含testpackage目录的那个目录。
  4. 在包的__init__.py中,导入你想要提供的模块。
  5. 从sub1.sub2导入子模块
    1. 如果最顶层的目录在PYTHONPATH上,并且这些目录及其子目录都有它们的__init__.py文件,那么这就是你从任何子目录中导入所需的全部内容。

测试项目目录是包含可执行文件、文档、软件包等的基本目录。testpackage/目录中有一个__init__.py文件,它是空的。因此,我应该从__init__.py中显式地导入其他模块。另外,我应该在testsubs目录中放置一个__init__.py文件,它会导入testsubsmod.py,并且我应该从testpackage/init.py中导入testsubs。 - nfarrar
关于 _init.py_ 文件:如果“TestProject”路径在您的PYTHONPATH中,则顶层init文件需要在“testpackage”的目录级别上(就像您现在所拥有的那个)。然后,您可以编写例如“from testpackage import testmod”的代码。 - ThomasH

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