如何在Python中进行相对导入?
想象一下这个目录结构:
app/ __init__.py sub1/ __init__.py mod1.py sub2/ __init__.py mod2.py
我编码mod1
,我需要从mod2
导入的东西。 我应该怎么做?
我试过from ..sub2 import mod2
但我得到一个“尝试相对导入非包”。
我googlesearch,但只发现“ sys.path
操纵”黑客。 有没有一个干净的方式?
编辑:我所有的__init__.py
目前是空的
编辑2:我试图做到这一点,因为sub2包含通过子包( sub1
, subX
等)共享的类。
编辑3:我正在寻找的行为是相同的PEP 366 (谢谢约翰乙)
每个人似乎都想告诉你你应该做什么,而不仅仅是回答这个问题。
问题在于,通过将mod1.py作为parameter passing给解释器,将模块作为“__main__”运行。
从PEP 328 :
相对导入使用模块的__name__属性来确定模块在包层次结构中的位置。 如果模块的名称不包含任何包信息(例如,它被设置为'__main__'),那么无论模块实际位于文件系统的哪个位置,相对导入都会被parsing为模块是顶级模块。
在Python 2.6中,他们增加了相对于主模块引用模块的能力。 PEP 366描述了这种变化。
更新 :根据Nick Coghlan,推荐的替代方法是使用-m开关在软件包内部运行模块。
main.py setup.py app/ -> __init__.py package_a/ -> __init__.py module_a.py package_b/ -> __init__.py module_b.py
- 你运行
python main.py
-
main.py
确实:import app.package_a.module_a
-
module_a.py
不import app.package_b.module_b
或者2或3可以使用: from app.package_a import module_a
只要你在你的PYTHONPATH中有app
,这将工作。 main.py
可以在任何地方。
所以你编写一个setup.py
将整个应用程序包和子包复制(安装)到目标系统的python文件夹,然后将main.py
到目标系统的脚本文件夹。
这是对我有用的解决scheme:
我from ..sub2 import mod2
执行相对from ..sub2 import mod2
,然后,如果我想运行mod1.py
则转至app
的父目录,并使用python -m开关以python -m app.sub1.mod1
运行模块python -m app.sub1.mod1
。
相对导入出现此问题的真正原因是,相对导入通过使用模块的__name__
属性来工作。 如果模块正在直接运行,则__name__
被设置为__main__
,并且不包含有关包结构的任何信息。 而且,这就是为什么python抱怨relative import in non-package
错误的relative import in non-package
。
因此,通过使用-m开关,您可以将包结构信息提供给python,通过它可以成功地parsing相对导入。
在进行相对导入时,我多次遇到这个问题。 而且,在阅读所有以前的答案之后,我仍然无法弄清楚如何以一种干净的方式解决问题,而无需在所有文件中放置样板代码。 (虽然一些评论真的有用,感谢@ncoghlan和@XiongChhiamiov)
希望这可以帮助那些与import相关的问题,因为通过PEP实在不好玩。
“Guido将包内的运行脚本视为反模式”(拒绝的PEP-3122 )
我花了很多时间试图find一个解决scheme,在Stack Overflow上阅读相关的post,并对自己说:“一定有更好的办法!”。 看起来没有。
def import_path(fullpath): """ Import a file with full path specification. Allows one to import from anywhere, something __import__ does not do. """ path, filename = os.path.split(fullpath) filename, ext = os.path.splitext(filename) sys.path.append(path) module = __import__(filename) reload(module) # Might be out of date del sys.path[-1] return module
我正在使用这个片段从path导入模块,希望有所帮助
这是解决100%:
- 应用程序/
- main.py
- 设置/
- local_setings.py
在app / main.py中导入settings / local_setting.py:
main.py:
import sys sys.path.insert(0, "../settings") try: from local_settings import * except ImportError: print('No Import')
用例子解释nosklo's
答案
注意:所有的__init__.py
文件都是空的。
main.py app/ -> __init__.py package_a/ -> __init__.py fun_a.py package_b/ -> __init__.py fun_b.py
应用程序/ package_a / fun_a.py
def print_a(): print 'This is a function in dir package_a'
应用程序/ package_b / fun_b.py
from app.package_a.fun_a import print_a def print_b(): print 'This is a function in dir package_b' print 'going to call a function in dir package_a' print '-'*30 print_a()
main.py
from app.package_b import fun_b fun_b.print_b()
如果你运行$ python main.py
它会返回:
This is a function in dir package_b going to call a function in dir package_a ------------------------------ This is a function in dir package_a
- main.py确实:
from app.package_b import fun_b
- fun_b.py
from app.package_a.fun_a import print_a
所以文件夹中的package_b
使用文件夹package_a
,这是你想要的。 对??
这不幸是一个sys.path破解,但它工作得很好。
我遇到了另一个层面的问题:我已经有了一个指定名称的模块,但它是错误的模块。
我想要做的是以下内容(我正在使用的模块是module3):
mymodule\ __init__.py mymodule1\ __init__.py mymodule1_1 mymodule2\ __init__.py mymodule2_1 import mymodule.mymodule1.mymodule1_1
请注意,我已经安装mymodule,但在我的安装我没有“mymodule1”
我会得到一个ImportError,因为它试图从我安装的模块导入。
我试图做一个sys.path.append,并没有工作。 什么工作是一个sys.path.insert
if __name__ == '__main__': sys.path.insert(0, '../..')
这种黑客,但一切工作! 所以请记住,如果你想决定覆盖其他path,那么你需要使用sys.path.insert(0,pathname)来使它工作! 这对我来说是一个非常令人沮丧的障碍,所有人都说使用“append”函数到sys.path,但是如果你已经定义了一个模块(我发现这是非常奇怪的行为)
让我把这个放在这里供我自己参考。 我知道这不是一个好的Python代码,但是我需要一个脚本来处理一个正在处理的项目,我想把这个脚本放在scripts
目录中。
import os.path import sys sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
正如@EvgeniSergeev在OP的评论中所说的,你可以在任意位置用.py
文件导入代码:
import imp foo = imp.load_source('module.name', '/path/to/file.py') foo.MyClass()
这是从这个SO回答 。
看看http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports 。 你可以做
from .mod1 import stuff
从Python文档中 ,
在Python 2.5中,可以使用
from __future__ import absolute_import
指令将导入行为切换到绝对from __future__ import absolute_import
。 这个绝对导入行为将成为未来版本的默认值(可能是Python 2.7)。 一旦绝对导入是默认import string
将始终find标准库的版本。 build议用户应该尽可能地开始使用绝对导入,所以最好from pkg import string
代码中的from pkg import string
开始编写代码
我发现将“PYTHONPATH”环境variables设置为顶层文件夹更为简单:
bash$ export PYTHONPATH=/PATH/TO/APP
然后:
import sub1.func1 #...more import
当然,PYTHONPATH是“全球性的”,但它并没有给我带来麻烦。
除了John B所说的之外,似乎设置__package__
variables应该有所帮助,而不是改变__main__
,这可能会导致其他事情。 但是就我所能testing的来说,它并不完全正常工作。
我也有同样的问题,PEP 328或者366都不能完全解决这个问题,就我所知,这两者在一天结束的时候都需要将包的头部包含在sys.path
。
我还应该提到,我没有find如何格式化应该进入这些variables的string。 是"package_head.subfolder.module_name"
还是什么?
假设你在顶层运行,那么在mod1
使用:
import sub2.mod2
代替
from ..sub2 import mod2
为什么你甚至需要这个? 你为什么不把它导入
from app.sub2 import mod2
我认为你必须问自己:
- 为什么我需要这样做?
- 我的包装分离做得好吗?
我不知道你为什么要这样做的背景。 但对我来说,一个更清洁的devise应该是具有以下软件包结构:
app/ __init__.py sub1/ __init__.py mod1.py sub12/ __init__.py mod2.py
那么你只需要做:
from sub12 import mod2
不要做相对import。 他们只会让你的代码更加脆弱。 如果按照Matej的build议进行绝对导入,那么对sys.path的更改和文件位置的更改就不那么容易了。