Python模块布局中子目录名与文件名相同
我习惯使用一种脚本语言,在这种语言中,模块的布局通常是这样的:
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 个回答
用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
这意味着包已经被导入。我想包的优先级并不总是会存在。最终,最好通过重命名文件或包来消除名称冲突。
你想要创建的是包。
在文件夹 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
还有很多其他的事情可以做哦!
你必须这样做:
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这样的测试库,它可以帮你自动发现模块并做一些其他的高级操作。
或者看看我一个小项目的实际例子:https://github.com/eevee/dywypi