在Python中导入包

82 投票
6 回答
110456 浏览
提问于 2025-04-17 11:18

我可能漏掉了一些明显的东西,不过无所谓:

当你在Python中导入一个像 os 这样的包时,你可以直接使用它的任何子模块或子包。例如,这样做是可以的:

>>> import os
>>> os.path.abspath(...)

但是,我有自己的包,结构如下:

FooPackage/
  __init__.py
  foo.py

而在这里,同样的逻辑却不奏效:

>>> import FooPackage
>>> FooPackage.foo
AttributeError: 'module' object has no attribute 'foo'

我到底哪里做错了呢?

6 个回答

20

你需要在你的包的 __init__.py 文件中添加 from . import foo 这一行代码。

73

当你导入 FooPackage 时,Python 会在 PYTHONPATH 指定的目录中查找,直到找到一个叫 FooPackage.py 的文件,或者一个名为 FooPackage 的文件夹,里面有一个叫 __init__.py 的文件。不过,找到这个包的文件夹后,Python 并不会自动扫描这个文件夹里的所有 .py 文件并导入它们。

这样做有两个原因。第一个原因是,导入一个模块会执行 Python 代码,这可能会消耗时间、内存,或者产生一些副作用。所以你可能只想导入 a.b.c.d,而不想把整个大包 a 都导入进来。包的设计者可以决定是否在 __init__.py 中明确导入它的模块和子包,这样它们就总是可用,或者让使用这个包的程序自己选择要加载哪些内容。

第二个原因稍微复杂一点,也很重要。如果没有明确的导入语句(无论是在 FooPackage/__init__.py 里,还是在使用这个包的程序中),Python 不一定知道应该把 foo.py 导入成什么名字。在不区分大小写的文件系统中(比如 Windows),这可能代表一个叫 fooFooFOOfOofoOFoOFOofOO 的模块。这些都是有效且不同的 Python 标识符,所以光凭文件名,Python 没法知道你想要哪个。因此,为了在所有系统上都能保持一致,Python 需要在某个地方有一个明确的导入语句来说明这个名字,即使在那些能够区分大小写的文件系统上也是如此。

43

你需要导入这个子模块:

import FooPackage.foo

你现在是在寻找 foo 这个东西,它在 FooPackage/__init__.py 这个文件里。你可以通过在 FooPackage/__init__.py 里加上 import FooPackage.foo as foo(或者 from . import foo)来解决这个问题,这样 Python 就能在那找到 foo 了。不过我建议你还是用我最开始的建议。

撰写回答