Pythonic模块组织 - 如何引用根目录中的文件?

7 投票
2 回答
7608 浏览
提问于 2025-04-17 07:49

我把我的Python代码放在一个叫“project”的文件夹里,所以我的代码文件都在project/*.py里。我想在里面创建一些子模块,比如:

project/code.py # where code lives
project/mymodule1  # where more code lives
project/mymodule2

每个模块目录都有自己的init.py文件,比如:

project/mymodule1/__init__.py

假设我在mymodule1文件夹里有一个文件“test.py”(路径是project/mymodule1/test.py),我想引用“code”里的某些东西,比如导入函数“myfunc”。

== project/mymodule1/test.py ==
from code import myfunc

问题是,如果用户没有把“project/”目录放到他们的PYTHONPATH里,“code”是找不到的。有没有办法避免这个问题,使用某种“相对路径”来导入myfunc,比如:

from ../code import myfunc

基本上,我不想强迫代码的用户去修改PYTHONPATH,除非我能在我的脚本里为他们程序化地做到这一点。我希望它能开箱即用。

这该怎么做呢?无论哪种解决方案都可以:程序化地修改PYTHONPATH,或者更理想的是,使用某种相对导入来引用“code”,因为即使我不知道“project/code.py”在用户电脑上的具体位置,我知道它相对于“myfunc”的位置。

编辑:能不能给我一个正确的包内导入的例子?我尝试从“mymodule1”去做:

from .. import foo

其中“foo”在code.py里,但这并不奏效。我在mymodule1里有init.py,所以:

project/code.py
project/mymodule1/__init__.py
project/mymodule1/module1_code.py

module1_code.py试图导入foo,这是在“code.py”中定义的一个函数。

编辑:我仍然感到困惑的是,即使在采纳了对我消息的回复中给出的例子,展示了project/sub1/test的层级结构后,你仍然不能“cd”进入sub1并执行“python test.py”让它工作。用户必须在包含“project”的目录下执行“import project.sub1.test”。我希望这能在用户所在的任何目录下都能工作。在这种情况下,用户必须执行位于project/sub1/中的“test.py”文件。所以测试案例是:

$ cd project/sub1
$ python test.py

这会导致错误:

ValueError: Attempted relative import in non-package

这该怎么解决呢?

谢谢。

2 个回答

1

避免这个问题的最好方法是把你的所有代码放在一个叫 src 的文件夹里,或者更好地,给它起个项目名称,比如 myproject,并在里面放一个 __init__.py 文件,这样你就可以这样做了。

from myproject import code

所以你的文件夹结构应该是这样的:

project
    main.py
    myproject
        __init__.py
        code.py
        module1
        module2

main.py 或者你给它起的其他名字不应该写太多代码,它应该从 myproject 中获取需要的模块并运行,比如:

from myproject import myapp
myapp.run()

可以看看这篇不错的 文章,了解如何整理你的 Python 项目。

4

在Python 2.5及以上版本中,这是可以实现的。你可以查看关于包内引用的文档。

有几点需要注意:

如果你希望用户能够在某个地方安装你的包,比如使用distutils或setuptools,那么project很可能已经在搜索路径中,这时你可以把相对导入改成from project.code import ...或者类似的写法。

如果用户要把你的包安装到一个非标准的目录(比如他们的主目录,或者其他不在sys.path中的地方),我认为让用户修改PYTHONPATH会更清晰,而不是在代码中去修改sys.path

如果你不打算让用户安装你的代码,比如他们只是解压源代码,进入project的上级目录,然后运行一个脚本,那么包内引用和相对导入应该可以正常工作。

编辑:应要求,这里有个例子:

假设包的结构如下:

project/
    __init__.py (empty)
    code.py
    sub1/
        __init__.py (empty)
        test.py

现在,project/code.py的内容是:

# code.py (module that resides in main "project" package)

def foo():
    print "this is code.foo"

project/sub1/test.py的内容是:

# test.py (module that resides in "sub1" subpackage of main "project" package)

from ..code import foo
foo()

所以,test.py从相对路径..code导入了名称foo,这使用了包内引用,能够让我们回到父级(就是..部分)中的code.py模块,这里是我们执行的sub1.test包。

要测试这个:

shell$ (cd to directory where "project" package is located)
shell$ python
Python 2.6.1 (r261:67515, Aug  2 2010, 20:10:18) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import project.sub1.test
this is code.foo

注意,from .. import X中的双点只是让你到达父包,但你可以在那个包内指定模块。

换句话说,在这种情况下,from .. import X等同于from project import X,因此X必须是project中的一个模块,或者是project/__init__.py中的一个类/函数/名称。

因此,from ..code import X等同于from project.code import X

撰写回答