我来过这里:
http://www.python.org/dev/peps/pep-0328/http://docs.python.org/2/tutorial/modules.html#packagesPython包:相对导入python相对导入示例代码不起作用python 2.5中的相对导入Python中的相对导入Python:禁用相对导入
还有很多我没有复制的URL,有些在SO上,有些在其他网站上,当时我以为我会很快找到解决方案。
永远重复出现的问题是:我如何解决这个“试图在非包中相对导入”消息?
ImportError: attempted relative import with no known parent package
我在pep-0328上创建了一个完全相同的包副本:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
导入是从控制台完成的。
我确实在相应的模块中创建了名为垃圾邮件和鸡蛋的函数。很自然,它不起作用。答案显然在我列出的第四个URL中,但对我来说都是校友。我访问的一个URL上有这样的回复:
相对导入使用模块的name属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“main”),则无论模块在文件系统上的实际位置如何,都会将相对导入解析为顶级模块。
上面的回答看起来很有希望,但对我来说都是象形文字。所以我的问题是,如何让Python不返回给我“尝试在非包中相对导入”?据推测,有一个答案涉及-m。
有人能告诉我为什么Python会给出这个错误消息,“非包”是什么意思,为什么以及如何定义“包”,以及准确的答案,让幼儿园的学生很容易理解。
因此,在与许多其他人一起对此吹毛求疵之后,我在本文中看到了Dorian B发布的一篇笔记,它解决了我在开发用于web服务的模块和类时遇到的具体问题,但我也希望能够在编写代码时使用PyCharm中的调试器工具对它们进行测试。要在自包含的类中运行测试,我将在类文件的末尾包含以下内容:
if __name__ == '__main__':
# run test code here...
但是如果我想在同一个文件夹中导入其他类或模块,那么我就必须将所有的导入语句从相对表示法更改为本地引用(即删除点(.))。但是在阅读了Dorian的建议后,我尝试了他的“一行”,结果成功了!我现在可以在PyCharm中进行测试,当我在另一个被测试的类中使用该类时,或者当我在web服务中使用它时,我可以将测试代码留在原地!
# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
from codex import Codex # these are in same folder as module under test!
from dblogger import DbLogger
else:
from .codex import Codex
from .dblogger import DbLogger
if语句检查我们是否将此模块作为main运行,或者它是否正在另一个作为main测试的模块中使用。也许这是显而易见的,但我在这里提供了这条注释,以防其他人对上述相关进口问题感到沮丧,可以利用它。
我有一个类似的问题,我不想更改Python模块搜索路径,并且需要从脚本中相对地加载模块(尽管上面BrenBarn很好地解释了“脚本不能相对地全部导入”)。
所以我使用了以下黑客。不幸的是,它依赖于imp模块自从3.4版本被弃用,转而使用importlib。(importlib也有可能吗?我不知道。)不过,目前黑客还是可以使用的。
从驻留在subpackage2文件夹中的脚本访问subpackage1中的moduleX成员的示例:
#!/usr/bin/env python3
import inspect
import imp
import os
def get_script_dir(follow_symlinks=True):
"""
Return directory of code defining this very function.
Should work from a module as well as from a script.
"""
script_path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
script_path = os.path.realpath(script_path)
return os.path.dirname(script_path)
# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)
# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST
一种更干净的方法似乎是修改Federico提到的用于加载模块的sys.path。
#!/usr/bin/env python3
if __name__ == '__main__' and __package__ is None:
from os import sys, path
# __file__ should be defined in this case
PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
因此,在与许多其他人一起对此吹毛求疵之后,我在本文中看到了Dorian B发布的一篇笔记,它解决了我在开发用于web服务的模块和类时遇到的具体问题,但我也希望能够在编写代码时使用PyCharm中的调试器工具对它们进行测试。要在自包含的类中运行测试,我将在类文件的末尾包含以下内容:
if __name__ == '__main__':
# run test code here...
但是如果我想在同一个文件夹中导入其他类或模块,那么我就必须将所有的导入语句从相对表示法更改为本地引用(即删除点(.))。但是在阅读了Dorian的建议后,我尝试了他的“一行”,结果成功了!我现在可以在PyCharm中进行测试,当我在另一个被测试的类中使用该类时,或者当我在web服务中使用它时,我可以将测试代码留在原地!
# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
from codex import Codex # these are in same folder as module under test!
from dblogger import DbLogger
else:
from .codex import Codex
from .dblogger import DbLogger
if语句检查我们是否将此模块作为main运行,或者它是否正在另一个作为main测试的模块中使用。也许这是显而易见的,但我在这里提供了这条注释,以防其他人对上述相关进口问题感到沮丧,可以利用它。
@布伦伯恩的回答说明了一切,但如果你像我一样,可能需要一段时间才能理解。这是我的案例,@BrenBarn的答案如何适用于它,也许它会帮助你。
案例
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
使用我们熟悉的示例,并在其中添加moduleX.py对..的相对导入。。模块A。假设我尝试在导入moduleX的subpackage1目录中编写测试脚本,但随后出现了OP描述的可怕错误。
解决方案
将测试脚本移动到与包相同的级别并导入package.subpackage1.moduleX
解释
如前所述,相对导入是相对于当前名称进行的。当我的测试脚本从同一目录导入moduleX时,moduleX中的模块名为moduleX。当遇到相对导入时,解释器无法备份包层次结构,因为它已经位于顶部
当我从上面导入moduleX时,moduleX内部的名称是package.subpackage1.moduleX,可以找到相对的导入