• python的相对引用问题


    背景

    现象

    遇到一个坑,import相对路径引起的。
    我有一个如下的文件结构:

    test/
        __init__.py # 此文件为空,代表test是一个package
        m1.py
        m2.py
    

    m1.py文件内容如下:

    # m1.py
    a = 1
    

    我想在m2.py文件中使用m1的变量,内容如下:

    # m2.py
    from .m1 import a
    
    b = a + 1
    
    if __name__ == '__main__':
        print(b)
    

    此时运行m2.py会报如下错误:

    itscs-MacBook-Pro:test itsc$ python m2.py 
    Traceback (most recent call last):
      File "m2.py", line 1, in <module>
        from .m1 import a
    ModuleNotFoundError: No module named '__main__.m1'; '__main__' is not a package
    

    原因

    python的两中文件执行方式

    python可以将文件以两种方式执行:

    python test/m2.py 
    
    python -m test.m2 
    

    此处为第一种,执行python m2.py的意思是将m2.py当做脚本执行。

    相对导入机制

    机制1:

    python的模块(module)有一个__package__属性,如果这个属性存在并且有值,相对导入(import)就会基于这个属性去导入,而不是基于__name__属性,参见PEP 0366。使用脚本方式执行时,__package__属性值为None,那么将会基于__name__属性去做相对导入;而执行python -m test.m2时,__package__属性值为模块所在包路径,此处是:test,此时将通过该test去做相对导入。

    机制2:

    每一个模块都有一个__name__属性,它代表了「模块名」,例如此处的m2,当运行python m2.py时:

    模块里的代码会被执行,就好像你导入了模块一样,但是 __name__ 被赋值为 __main__

    所以报错里面提示的是__main__.m1找不到,而不是__m2__.m1找不到。

    以上两个机制解释了为什么报错。首先,使用脚本方式执行时__package__属性值为None,那么将会基于__name__属性去做相对导入,而此时的__name__ 被赋值为 __main__,使用__main__去做相对导入是会失败的,应为它和m1并没有构成相对关系。

    附:python文档中有这样一句解释:

    请注意,相对导入是基于当前模块的名称进行导入的。由于主模块的名称总是 "__main__" ,因此用作Python应用程序主模块的模块必须始终使用绝对导入。

    解决办法

    根据上面的原因分析,有两种解决办法:

    1.使用相对路径,使用带-m参数方式调用。

    2.使用绝对路径,使用绝对路径就避开了相对路径那些问题。

    方法一

    将其当做一个module来运行(运行一个module需要在package外面运行)(参考):

    1.将当前路径切换到test文件夹的上级目录目录(不是test里面)

    2.执行如下命令:

    python -m test.m2
    

    注意:首先,多了一个-m选项;其次,路径从test文件夹开始写的,也就是m1和m2的父目录;最后,m2不带.py后缀。此时__package__属性值为test,将通过该test去做相对导入,就没问题。

    方法二

    如前所述,相对引用依赖模块名,那么不使用相对依赖就没有这个限制,将m2.py文件内容修改如下:

    from m1 import a
    
    b = a + 1
    
    if __name__ == '__main__':
        print(b)
    

    相对于之前的版本,只是把m1前面的点号去除了,然后再次运行(目录切换到test里面):

    itscs-MacBook-Pro:test itsc$ python m2.py
    2
    

    用这个方法的前提是m1这个模块在python的搜索路径中是唯一的。如果不唯一,就需要把test文件夹的路径导入到PYTHONPATH中。

    参考

    Relative imports in Python 3

    https://stackoverflow.com/a/7506029/6381223

    PEP 366 -- Main module explicit relative imports

    What's the purpose of the “package” attribute in Python?

    命令行与环境

    Execution of Python code with -m option or not

  • 相关阅读:
    Extjs打开window窗口自动加载html网页
    CSS预处理器之SASS用法指南
    HmacSHA256摘要算法
    Base64编解码
    孔子困于陈蔡故事(转载)
    我的2019
    给Oracle字段和表加注释
    【JDBC】使用properties连Oracle数据库,使用DatabaseMetaData获取字段的注释
    [JDBC]查询结果集把字段名和字段值一起竖向输出
    [Java/Reflect]使用反射机制获得一个对象的属性名和属性值
  • 原文地址:https://www.cnblogs.com/shanchuan/p/12649737.html
Copyright © 2020-2023  润新知