在Python解释器中运行时出现“ImportError: attempted relative import with no known parent package”错误提示

15
我正在使用 Flask 的 蓝图 功能创建一个模块化的应用程序。因此,我的目录结构如下:
project
    __init__.py
    config.py
    mould.py
    modules
        __init__.py
        core
            __init__.py
            core.py
            db.py
            models.py

这里提到的“模块目录”与Python模块不同,它们是为了给我的项目(核心模块、foo模块、bar模块等)提供模块化结构。现在,模块目录中的每个文件夹(和其中同名的模块,如core.core)都通过以下方式在我的主Flask应用程序(mould.py)中进行动态导入:
for item in os.listdir("modules"):
    if not os.path.isfile("modules" + os.sep + item) and not item.startswith("__"):
        ppath = "modules" + "." + item
        fullpath = "modules" + "." + item + "." + item
        module = importlib.import_module(fullpath)
        app.register_blueprint(module.app)
        print("Registered: " + ppath)

由于这个原因,我无法在像db.py这样的模块脚本中执行此操作。
import models

由于整个模块在项目级别下执行时会产生路径错误,因此我不得不这样做:

from . import models

这解决了问题,我成功地导入了所有模块。但是,当我进入核心模块目录进行故障排除并启动Python解释器时,它不允许我导入db模块:

>>> import db
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "db.py", line 7, in <module>
    from . import models
ImportError: attempted relative import with no known parent package

有没有解决办法?这样,我既可以在代码中成功导入db模块,也可以在解释器中成功导入。


只是略微相关,您可能会发现pkgutil.walk_packages很有用。使用方式类似于walk_packages(modules.__path__, modules.__name__ + '.')。由于看起来您正在使用Python2,我建议在所有文件的顶部添加from __future__ import absolute_import - anthony sottile
3
为什么需要能够执行 import db 而不是 from modules.core import db - univerio
2个回答

5

我知道我有点晚了,但我认为我已经找到了解决这个问题的方法。希望这对正在开发大型Python项目的其他人有所帮助。

诀窍是尝试一种导入格式,如果第一种失败,则回退到另一种格式。

方法1

db.py

try:
    # Assume we're a sub-module in a package.
    from . import models
except ImportError:
    # Apparently no higher-level package has been imported, fall back to a local import.
    import models

优点是这种方法非常简单,但不适合大规模应用(模块名称会重复)。通过编程方式进行导入可以提高可扩展性。

方法2(不建议使用)

db.py

import importlib

root = 'project.modules.core'
my_modules = ['core', 'models']
for m in my_modules
    try:
        globals()[m] = importlib.import_module(root + '.' + m)
    except ImportError:
        globals()[m] = importlib.import_module(m)

globals() 是全局符号表。

当然,现在这个功能需要在每个模块中进行复制。我不确定这是否真的比第一种方法更好。但是,您可以将此逻辑分离到其自己独立的包中,该包位于 pythonpath 上。

第三种方法

package_importer.py

import importlib

def import_module(global_vars, root, modules):
    for m in modules
        try:
            global_vars[m] = importlib.import_module(root + '.' + m)
        except ImportError:
            global_vars[m] = importlib.import_module(m)

db.py

import package_importer

root = 'project.modules.core'
my_modules = ['core', 'models']
package_importer.import_module(globals(), root, my_modules)

6
刚刚给我点踩的人,能否解释一下为什么? 我不是说我的答案完美或者最好,但如果有更好的方法,我想知道。 - ErikusMaximus

2

这可能有点过时了,但也许其他人会从我的答案中受益。由于Python2和Python3具有不同的默认导入行为,因此您必须区分这两个Python版本。

Python 2.X import models 的默认行为是首先查找相对路径,然后再查找绝对路径。因此它应该可以工作。

然而,在 Python 3.X 中,import models 的默认行为是仅在绝对路径(称为绝对导入)中查找模型。当前包 core 被跳过,因为模块 db sys.path 搜索路径中无法找到,所以它会抛出一个错误。要解决此问题,您必须使用带有点的导入语句 from . import models,以明确您正在尝试从相对目录导入。

如果您想了解更多关于导入Python模块的知识,请从以下关键词开始研究:模块搜索路径Python包导入相对包导入


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接