• 执行代码出现ImportError:attempted relative import with no known parent package


    前言

    在这篇文章中,我将会解析 ImportError: attempted relative import with no known parent package 这个异常的原因。当你在运行的python脚本。使用了相对引用方式 (类似import ..module) 去引用包时,可能会出现这个异常。

    让我们来看看发生这个异常的例子。

    问题

    假设你有以下目录结构:

    project
    ├── config.py
    └── demos
        ├── __init__.py
        └── demo.py

    config.py 中包含一些应该在 demo.py 中使用的变量

    • project/config.py
    count = 5
    • project/demos/demo.py
    from .. import config
    print("The value of config.count is {0}".format(config.count))

    当我们尝试运行demo.py时,会遇到以下错误:

    E:project> python demos/demo.py
    Traceback (most recent call last):
      File "demos/demo.py", line 1, in <module>
        from .. import config
    ImportError: attempted relative import with no known parent package

    python解释器抛出了没有父级包的异常。为什么?

    让我们看看python解释器是如何解析相关模块。从 PEP 328 中,我们找到了关于 the relative imports(相对引用)的介绍:

    Relative imports use a module’s __name__ attribute to determine that module’s position in the package hierarchy. If the module’s name does not contain any package information (e.g. it is set to __main__ ) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
    
    相对导入通过使用模块的 __name__ 属性来确定模块在包层次结构中的位置。如果该模块的名称不包含任何包信息(例如,它被设置为 __main__ ),那么相对引用会认为这个模块就是顶级模块,而不管模块在文件系统上的实际位置。

    换句话说,解决模块的算法是基于__name____package__变量的值。大部分时候,这些变量不包含任何包信息 —- 比如:当 __name____main__ 和 __package__ = None 时,python解释器不知道模块所属的包。在这种情况下,相对引用会认为这个模块就是顶级模块,而不管模块在文件系统上的实际位置。

    为了演示这个原理,我们来更新一下代码:

    • project/config.py
    print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
    count = 5
    • project/package/demo.py
    print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
    from .. import config
    print("The value of config.count is {0}".format(config.count))

    再次尝试运行一下,会得到以下输出:

    E:project> python demos/demo.py
    __file__=demos/demo.py      | __name__=__main__    | __package__=None
    Traceback (most recent call last):
      File "demos/demo.py", line 3, in <module>
        from .. import config
    ImportError: attempted relative import with no known parent package

    正如我们所看到的,python解释器没有关于模块所属的包的任何信息( __name__ = __main__ 和 __package__ = None ),因此它抛出了找不到父级包的异常。

    解决方案一

    • 我们通过在其中创建一个新的空 __init__.py 文件来将项目目录转换为一个包。

    • 我们在项目目录的父目录中创建一个文件 main.py

    toplevel
    ├── main.py
    └── project
      ├── __init__.py
      ├── config.py
      └── demos
          ├── __init__.py
          └── demo.py
    • toplevel/main.py
    print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
    import project.demos.demo

    执行一下新的示例,输出如下:

    E:	oplevel>python main.py
    __file__=main.py                             | __name__=__main__             | __package__=None
    __file__=E:	oplevelprojectdemosdemo.py   | __name__=project.demos.demo   | __package__=project.demos
    __file__=E:	oplevelprojectconfig.py       | __name__=project.config       | __package__=project
    The value of config.count is 5

    在 main.py 中导入 project.demos.demo 会设置相对引用的包信息( __name__ 和 __package__ 变量)。现在,python解释器可以成功解析 projectdemosdemo.py 中的相对引用了。

    解决方案二

    • 我们通过在 project 文件夹中创建一个新的空 __init__.py 来将 project 目录转换为一个包。

    • 在 toplevel 目录下通过 -m 参数来调用python解释器,去执行 project.demos.demo[1]

    toplevel
    └── project
      ├── __init__.py
      ├── config.py
      └── demos
          ├── __init__.py
          └── demo.py

    再次执行:

    E:	oplevel>python -m project.demos.demo
    __file__=E:	oplevelprojectdemosdemo.py   | __name__=__main__        | __package__=project.demos
    __file__=E:	oplevelprojectconfig.py       | __name__=project.config  | __package__=project
    The value of config.count is 5

    运行该命令将自动设置包信息(__package__变量)。现在,python解释器可以成功解析 project demosdemo.py 中的相对引用了(甚至认为 __name __ = __ main__ )。

    注意使用 -m 参数的时候,后面指定的执行文件没有 .py 后缀。

    注2:

    实际使用中还有一个可能会报出这种错误, 就是执行代码的路径不对。 

    E:	oplevel>python -m project.demos.demo

    一定要在project的上一级目录执行这个命令。

  • 相关阅读:
    序列、元组、列表(基本的增、删、改、查)
    Python基础运算符(算数、比较、赋值、逻辑、成员)
    2015年9月14日记事
    2014年3月31日梦
    华为S5700系列交换机配置文件导出、导入
    C语言单链表简单实现(简单程序复杂化)
    北邮《大学英语2》第三次阶段作业带答案
    C++走向远洋——30(六周,项目一1.0)
    C++走向远洋——29(长方柱类)
    C++走向远洋——28(项目三,时间类,2)
  • 原文地址:https://www.cnblogs.com/scarecrow-blog/p/10419330.html
Copyright © 2020-2023  润新知