Python模块布局中子目录名与文件名相同

1 投票
3 回答
1803 浏览
提问于 2025-04-18 14:23

我习惯使用一种脚本语言,在这种语言中,模块的布局通常是这样的:

lib/Foo.pm
lib/Foo/Bar.pm
lib/Foo/Baz.pm

这里有一个叫 Foo.pm 的文件,然后有一个名为 Foo 的文件夹,文件夹下面放着子模块。

在 Python 中,我试着模仿这种布局,像这样:

modules/foo.py
modules/foo/bar.py
modules/foo/baz.py

但是,这样做不行,因为当我执行以下代码时:

from foo import Foo

Python 会认为我是在导入一个名为 'foo' 的文件夹,而不是 foo.py 文件。我尝试过调整 init.py 的设置,但没有成功。

有没有办法解决这个问题?我觉得 Python 不能区分文件夹 foo 和文件 foo.py 这点非常烦人。

编辑: 我想我漏掉了一个重要的部分。我是通过单元测试来引用 modules/ 这个目录,所以整个目录结构看起来是这样的:

modules/foo.py
modules/foo/bar.py
modules/foo/baz.py
tests/unit.py

在 unit.py 中,我有以下内容:

#!/usr/bin/python

import sys
import os

findbin = os.path.abspath(os.path.dirname(sys.argv[0]))
sys.path.append(findbin + "/../modules")

from foo import Foo

obj = Foo()

当我运行这个时,我得到了:

from foo import Foo
ImportError: cannot import name Foo

3 个回答

0

用Python创建包可以通过 __init__.py 文件来实现,但理解导入的优先级很重要。这个命令

from foo import Foo

会在 sys.path 中寻找一个叫 foo 的东西,然后再在 foo 里面寻找一个叫 Foo 的东西。举个例子,假设你在 foo 目录下没有任何 __init__.py 文件,而你的 foo.py 文件里面包含了

class Foo:
    def __init__(self):
        print 'hello'

从模块目录执行命令 from foo import Foo 是可以成功的。

>>> from foo import Foo
>>> f = Foo()
hello

如果你想让目录 foo 成为一个包(也就是一组相关模块的集合),那么你可以在 foo 目录里放一个空的 __init__.py 文件。不过这样会让 import 命令变得有些模糊。我没有找到任何文档说包的导入优先级比模块高,但你可以通过以下方式验证Python是否在导入包,

import foo
>>> foo.__file__
foo/__init__.py

这意味着包已经被导入。我想包的优先级并不总是会存在。最终,最好通过重命名文件或包来消除名称冲突。

5

你想要创建的是包。

在文件夹 foo 里放一个空文件,名字叫 __init__.py。这样,Python 就会把 foo 当作一个包来处理。如果 modules 也想当作一个包,就在里面也放一个空的 __init__.py 文件。

所以根据你的问题,如果包设置得正确,并且你有这样的结构:

modules/__init__.py
modules/foo/__init__.py
modules/foo/bar.py
modules/foo/baz.py

你可以做这些事情:

import module
from module import foo
from module.foo import bar
import module.foo.bar as foobar

还有很多其他的事情可以做哦!

1

必须这样做:

modules/foo/__init__.py  # instead of foo.py
modules/foo/bar.py
modules/foo/baz.py

Perl的布局不太好用;如果一个模块有子模块,根模块和它们是平行的。

另外,有些Python开发者认为你应该把__init__.py文件留空(或者至少保持简单),因为每次你导入任何子模块时,__init__.py都是第一个被导入的。

如果__init__.py对你没有用,可能你还有其他问题,特别是当你真的有一个modules/目录时,不过我没有更多的信息就没法判断具体情况 :)


明白了。不要去动sys.path;这总是意味着你在给自己增加麻烦。(你需要修改sys.path才能导入别人的代码吗?那为什么你自己的代码就要这样做呢,明明就在那儿?)

直接把它们放在你的源代码根目录下,比如project.git/foo/bar.py。只要你从根目录运行测试工具(或者其他的),就可以毫无问题地导入它们。

你也可以试试像pytest这样的测试库,它可以帮你自动发现模块并做一些其他的高级操作。

另见:Python项目的文件系统结构

或者看看我一个小项目的实际例子:https://github.com/eevee/dywypi

撰写回答