想象一下这个目录结构:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
我正在编写 mod1
,我需要从 mod2
导入一些内容。我该怎么做?
我尝试了 from ..sub2 import mod2
,但我得到了“尝试在非包中进行相对导入”。
我四处搜索,但只发现“sys.path
操纵”黑客。没有干净的方法吗?
编辑:我所有的 __init__.py
目前都是空的
Edit2:我正在尝试这样做,因为 sub2 包含跨子包(sub1
、subX
等)共享的类。
Edit3:我正在寻找的行为与 PEP 366 中描述的行为相同(感谢 John B)
这是对我有用的解决方案:
我将相对导入作为 from ..sub2 import mod2
进行,然后,如果我想运行 mod1.py
,则转到 app
的父目录并使用 python -m 开关作为 python -m app.sub1.mod1
运行模块。
相对导入出现此问题的真正原因是相对导入通过获取模块的 __name__
属性来工作。如果模块正在直接运行,则 __name__
设置为 __main__
,它不包含任何有关包结构的信息。而且,这就是 python 抱怨 relative import in non-package
错误的原因。
因此,通过使用 -m 开关,您可以向 python 提供包结构信息,通过它可以成功解析相关导入。
我在做相对导入时多次遇到这个问题。而且,在阅读了所有先前的答案之后,我仍然无法弄清楚如何以一种干净的方式解决它,而无需将样板代码放入所有文件中。 (虽然有些评论真的很有帮助,但感谢@ncoghlan 和@XiongChiamiov)
希望这对正在与相关进口问题作斗争的人有所帮助,因为通过 PEP 真的不好玩。
-m
旨在解决这一问题。
from . import some_module
。
$ PWD
是它的父目录比如$ python -m app.main
。为清楚起见,$ python -m <main_directory>.<script_with_relative_imports>
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
到目标系统的脚本文件夹。
“Guido 将包中正在运行的脚本视为反模式”(拒绝 PEP-3122)
我花了很多时间试图找到解决方案,阅读 Stack Overflow 上的相关帖子,并对自己说“一定有更好的方法!”。好像没有。
-m
开关运行它:python -m app.sub1.mod1
或从顶级脚本调用 app.sub1.mod1.main()
(例如,从 setup.py 中定义的 setuptools 的 entry_points 生成)。
这是100%解决的:
应用程序/ main.py
主文件
设置/local_settings.py
local_settings.py
在 app/main.py 中导入 settings/local_setting.py:
主要.py:
import sys
sys.path.insert(0, "../settings")
try:
from local_settings import *
except ImportError:
print('No Import')
sys.path.insert(0, "../settings")
,然后使用 from local_settings 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()
主文件
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 确实:从 app.package_b 导入 fun_b
fun_b.py 从 app.package_a.fun_a 导入 print_a
所以文件夹 package_b
中的文件使用了文件夹 package_a
中的文件,这就是您想要的。正确的??
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
我正在使用此代码段从路径中导入模块,希望对您有所帮助
不幸的是,这是一个 sys.path hack,但效果很好。
我在另一层遇到了这个问题:我已经有一个指定名称的模块,但它是错误的模块。
我想做的是以下(我正在使用的模块是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, '../..')
有点像黑客,但一切正常!所以请记住,如果您希望自己的决定覆盖其他路径,那么您需要使用 sys.path.insert(0, pathname) 让它工作!这对我来说是一个非常令人沮丧的症结所在,很多人说要对 sys.path 使用“附加”功能,但是如果您已经定义了一个模块,那将不起作用(我觉得这是非常奇怪的行为)
sys.path.append('../')
适合我(Python 3.5.2)
让我把它放在这里供我自己参考。我知道这不是好的 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()
这取自 this SO answer。
看看http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports。你可以做
from .mod1 import stuff
从 Python doc,
在 Python 2.5 中,您可以使用 from __future__ import absolute_import 指令将导入的行为切换为绝对导入。这种绝对导入行为将成为未来版本(可能是 Python 2.7)中的默认行为。一旦绝对导入成为默认值,导入字符串将始终找到标准库的版本。建议用户应尽可能开始使用绝对导入,因此最好在代码中从 pkg 导入字符串开始编写
我发现将“PYTHONPATH”环境变量设置到顶部文件夹更容易:
bash$ export PYTHONPATH=/PATH/TO/APP
然后:
import sub1.func1
#...more import
当然,PYTHONPATH 是“全球性的”,但它还没有给我带来麻烦。
virtualenv
让您管理导入语句的方式。
除了 John B 所说的之外,似乎设置 __package__
变量应该会有所帮助,而不是更改可能会搞砸其他事情的 __main__
。但据我测试,它并不能完全发挥应有的作用。
我有同样的问题,PEP 328 或 366 都没有完全解决问题,因为据我所知,到一天结束时,都需要将包的头部包含在 sys.path
中。
我还应该提到,我没有找到如何格式化应该进入这些变量的字符串。是 "package_head.subfolder.module_name"
还是什么?
您必须将模块的路径附加到 PYTHONPATH
:
export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"
sys.path
大致相同,因为 sys.path
是从 PYTHONPATH
初始化的
sys.path
需要在源代码中硬编码,而 PYTHONPATH
是一个环境变量,可以导出。
此方法查询并自动填充路径:
import os
import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
os.sys.path.insert(1, parentdir)
# print("currentdir = ", currentdir)
# print("parentdir=", parentdir)
一个 hacky 方法是在运行时将当前目录附加到 PATH 中,如下所示:
import pathlib
import sys
sys.path.append(pathlib.Path(__file__).parent.resolve())
import file_to_import # the actual intended import
与此问题的另一个解决方案相比,它使用 pathlib
而不是 os.path
。
不定期副业成功案例分享
-m
开关在包内运行模块,而不是直接指定它们的文件名。from sub2 import mod2
。然后,要从应用程序内运行 mod1,请执行python -m sub1.mod1
。