Pythonic模块组织 - 如何引用根目录中的文件?
我把我的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 个回答
避免这个问题的最好方法是把你的所有代码放在一个叫 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 项目。
在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
。