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

6 投票
2 回答
5915 浏览
提问于 2025-04-17 00:12

首先,我想说的是,我在过去一周做了很多研究,但还没有找到这些问题的实际答案——只是一些模糊的回答,根本没有解释清楚发生了什么。如果我只是错过了我想要的信息,我很抱歉——请直接告诉我正确的方向。

我的目录结构是:

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
    • 这样做会很好,这样我就可以用与标准库或twisted重名的模块命名(比如testpackage.logging),而不会导致错误(我不想把自己的模块命名成customerlogging,而不是直接用mypackage.logging)

然后是我的问题:

  1. Python在处理位于pythonpath中的包和模块的导入时,和从当前目录导入时有什么不同吗?
  2. 为什么import testpackage不能让我访问testpackage.testmod?当我导入os时,我可以访问os.path(等等)。
  3. 对于一个包,我应该在基础目录中只使用一个init.py,还是应该在后续目录中嵌套它们?
  4. 我如何指定包名来导入模块?例如,从testmod.py,我想导入testpackage.testmod2,而不是仅仅导入testmod2。
  5. 从子目录导入子模块的正确方法是什么?
    1. 我看到的唯一解决方案是从init.py将那个目录添加到pythonpath中,但我不知道这是否是正确的方法。

提前感谢你的帮助。

2 个回答

0
  1. 不可以。当前目录只是被添加到了 PYTHONPATH 中。
  2. 首先,你需要在里面放一个 __init__.py 文件。其次,os.path 是因为 os 在导入 os.path。
  3. 你需要在每个目录里都有一个 __init__.py 文件,包括包含 testpackage 目录的那个目录。
  4. 在这个包的 __init__.py 文件中,导入你想要提供的模块。
  5. 从 sub1.sub2 导入 submodule
    1. 不,如果最上层的目录在 PYTHONPATH 中,并且这个目录和子目录都有它们的 __init__.py 文件,那么你只需要这样就可以从任何子目录导入。
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中导入它,这就是osos/__init__.py)所做的。这样,如果你导入testpackagetestpackage.testmod就会自动可用。

由于Python是跨平台的,实际上没有办法一致且自动地加载目录中的模块,因为某些文件系统是不区分大小写的(比如Windows!)。Python无法判断是加载os/path.py作为os.path还是os.Path,等等。


(3) 在一个包中,我应该只在基础目录使用一个__init__.py,还是应该在后续目录中嵌套它们?

每个子包都需要一个__init__.py。曾经讨论过是否可以去掉这个要求,但最终决定保留它。


(4) 我该如何指定包名来导入模块?也就是说,从testmod.py中,我想导入testpackage.testmod2而不是仅仅testmod2

这样做应该可以。只要确保你从顶层目录运行代码。如果当前目录是testpackagetestmod就不知道它在一个包里。

不过,推荐的方式是使用相对的包内导入:

from . import testmod2

这样可以避免名称冲突,如果有一个全局模块叫testmod2,你可以在你的包中使用知名模块的名称而不会出现问题。


(5) 从子子目录导入子模块的正确方法是什么?我看到的唯一解决方案是从__init__.py中将该目录添加到pythonpath,但我不知道这是否正确。

不,千万不要这样做!绝对不要在一个父目录已经在sys.path中的情况下,把一个目录添加到sys.path!这样可能会导致你的模块被加载两次,这是个坏事!

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

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

只要确保在testsubs/中创建一个__init__.py

撰写回答