python中相对路径导入的正确用法及错误避免方法

作者:

文章目录
  • 相对导入只适合包内模块去使用!!! 一个 .py 文件一旦使用相对导入,则直接运行该文件一定会报错:“ImportError: attempted relative import with no known parent package”。 正确的使用方法是: 包内的模块之间使用相对导入,包外的模块通过绝对路径导入去调用这个包中的模块。 【包内相对导入,包外绝对导入】 示例: 假设现有如下文件结构,main.py 与 package 在同一级目录。package 是一个文件夹(包),其中包含了模块 module1.py 和 module2.py 以及包的初始化文件__init__.py。即,main.py在包外,module1.py在包内。 main.pypackage/├── __init__.py├── module1.py└── module2.py  包内相对导入: 现在假设 package 内的模块 module1 相对导入了同级目录的模块 module2 的函数 func2 。 module1.py : # module1.py from .module2 import func2 包外间接调用: main.py : # main.py from package.module1 import func2 func2() module2.py:  # module2.py def func2(): print(“func2”) 则运行 main.py 的输出结果是: func2  如果你不知道什么是文件夹和包的区别,不知道什么是相对导入和绝对导入,以下是一些补充信息。
  • 文件夹/目录(Folder/Directory):一个操作系统级别的概念,用于存储文件和子文件夹。 脚本(Script):单个 .py 文件,可以包含函数、类等定义,但主要目的是执行一些操作。 模块(Module):单个 .py 文件,可以包含函数、类、变量等定义,用于实现特定的功能。主要用于代码复用和组织,可以被其他脚本或模块导入使用。 程序(Program):一个或多个脚本或模块组成的一个整体,用于完成一个完整的任务或功能。程序可以是一个简单的脚本,也可以是一个复杂的项目。通常会通过主程序 main.py 启动,主程序负责协调各个模块的运行。 包(Package):一个文件夹/目录,但是目录必须中包含一个特殊的文件 __init__.py。包可以包含子包,形成层次结构。 库(Library):一组模块或包的集合,通常用于实现特定的功能或功能集。库可以是标准库(Python 自带的库),也可以是第三方库(通过 pip 安装的库)。通常通过 import 语句导入使用。第三方库可以通过 pip 安装和管理。标准库有 math、os、sys 等。第三方库有 requests、numpy、pandas 等。 项目(Project):一个包含多个文件和目录的完整开发任务,用于实现一个完整应用程序。
  • 目录
    • 前言
    • 1. 基础概念辨析:模块、包、文件夹的区别
    • 2. 绝对路径和相对路径(Absolute & Relative)
      • 2.1 使用方法
      • 2.2 使用场景
      • 2.3 注意事项
    • 3. 基础的 import 语法
      • 3.1 导入方法1:导入整个模块
      • 3.2 导入方法2:导入模块中的所有对象
      • 3.3 导入方法3:导入模块中的指定对象
      • 3.4 导入方法4:别名导入
    • 4. 包的导入
      • 4.1 导入包中的模块(当前文件与包在同一级目录)
      • 4.2 导入包中的对象
      • 4.3 相对导入(仅适用于包内导入)
    • 5. 其它一些你可能需要的信息
      • 5.1 命名冲突
      • 5.2 模块查找路径
      • 5.3 循环依赖

    相对导入只适合包内模块去使用!!!

    一个 .py 文件一旦使用相对导入,则直接运行该文件一定会报错:“ImportError: attempted relative import with no known parent package”

    正确的使用方法是:

    包内的模块之间使用相对导入,包外的模块通过绝对路径导入去调用这个包中的模块。

    【包内相对导入,包外绝对导入】

    示例:

    假设现有如下文件结构,main.py 与 package 在同一级目录。package 是一个文件夹(包),其中包含了模块 module1.py 和 module2.py 以及包的初始化文件__init__.py。即,main.py在包外,module1.py在包内。

    main.py
    package/
    ├── __init__.py
    ├── module1.py
    └── module2.py

     包内相对导入:

    现在假设 package 内的模块 module1 相对导入了同级目录的模块 module2 的函数 func2 。

    module1.py :

    # module1.py
    from .module2 import func2

    包外间接调用:

    main.py :

    # main.py
    from package.module1 import func2
    func2()

    module2.py: 

    # module2.py
    def func2():
        print("func2")

    则运行 main.py 的输出结果是:

    func2 

    如果你不知道什么是文件夹和包的区别,不知道什么是相对导入和绝对导入,以下是一些补充信息。

    • 文件夹/目录(Folder/Directory):一个操作系统级别的概念,用于存储文件和子文件夹。
    • 脚本(Script):单个 .py 文件,可以包含函数、类等定义,但主要目的是执行一些操作。
    • 模块(Module):单个 .py 文件,可以包含函数、类、变量等定义,用于实现特定的功能。主要用于代码复用和组织,可以被其他脚本或模块导入使用。
    • 程序(Program):一个或多个脚本或模块组成的一个整体,用于完成一个完整的任务或功能。程序可以是一个简单的脚本,也可以是一个复杂的项目。通常会通过主程序 main.py 启动,主程序负责协调各个模块的运行。
    • 包(Package):一个文件夹/目录,但是目录必须中包含一个特殊的文件 __init__.py。包可以包含子包,形成层次结构。
    • 库(Library):一组模块或包的集合,通常用于实现特定的功能或功能集。库可以是标准库(Python 自带的库),也可以是第三方库(通过 pip 安装的库)。通常通过 import 语句导入使用。第三方库可以通过 pip 安装和管理。标准库有 mathossys 等。第三方库有 requestsnumpypandas 等。
    • 项目(Project):一个包含多个文件和目录的完整开发任务,用于实现一个完整应用程序。

    假设有一个包结构如下:

    package/
    ├── __init__.py
    ├── module1.py
    └── subpackage/
        ├── __init__.py
        └── module2.py

    module1中绝对导入 module2

    # module1.py
    from package.subpackage import module2
    
    module2.func2()

    module2 中绝对导入 module1

    # module2.py
    from package import module1
    
    module1.func1()

    module1中相对导入 module2

    # module1.py
    from .subpackage import module2
    
    module2.func2()

    module2中相对导入 module1

    # module2.py
    from .. import module1
    
    module1.func1()

    绝对导入的使用场景

    • 跨包导入:当需要从一个包导入另一个包中的模块时,绝对导入是更好的选择。
    • 清晰的路径:当模块结构复杂时,绝对路径更清晰,便于理解和维护。
    • 避免冲突:在大型项目中,绝对路径可以避免不同模块之间的路径冲突。

    相对导入的使用场景

    • 包内部模块导入:在包内部的模块之间导入时,相对导入可以使代码更简洁。
    • 包的位置可能变化:如果包的位置发生变化,相对导入可以减少路径调整的工作量。

    相对导入的限制

    • 不能在脚本中使用:如果直接运行脚本(如 python script.py),相对导入会报错。
    • 仅适用于包内部:相对导入只能在包内部的模块之间使用,不能在包外部使用。

    绝对导入的优点

    • 路径明确:绝对路径更清晰,便于理解和维护。
    • 避免冲突:在大型项目中,绝对路径可以避免不同模块之间的路径冲突。
    • 适用范围广:适用于导入项目中的任何模块,无论当前模块的位置如何。

    总是实在不行就用绝对导入,只要你的文件位置不会变来变去,包不出错的。

    先导入整个模块,再通过模块名访问指定对象。 模块中的所有对象(函数、类、变量等)都可以通过 模块名.对象名 的方式访问。例如:

    import 模块名
    模块名.函数名()

    优点:可以清楚地知道对象来自哪个模块 & 不会污染当前命名空间。

    缺点: 前缀冗长。

    一口气导入模块中的所有对象(函数、类、变量等),模块中的所有对象都可以使用原本的名字直接调用:

    from 模块名 import *
    函数名()

    优点:调用简洁。

    缺点:不清楚哪些对象被导入了 & 可能命名冲突。

    将指定的对象导入到当前命名空间中,可以直接使用对象名调用,而不需要加上模块名作为前缀。 

    from 模块名 import 函数名, 对象2, …
    函数名()

    优点:调用简洁。

    缺点是:可能命名冲突。

    为模块设置一个别名,使用别名来访问模块中的对象。

    为导入的对象设置别名,下面以函数对象为例。

    import 模块名 as 模块别名
    from 模块名 import 函数名 as 函数别名
    模块别名.函数名()
    函数别名()

    这种方法主要用于解决名字过长和命名冲突问题。

    如果模块位于一个包中,可以使用点号(.)来指定路径。

    示例: 假设当前文件结构如下:

    main.py

    package/
    ├── __init__.py
    ├── module1.py
    └── subpackage/
        ├── __init__.py
        └── module2.py

    ( __init__.py 文件不可以缺省!!不然你之后运行 main.py 会报错,例如:AttributeError: module 'package' has no attribute 'module1' )

    随后,在 main.py 中,可以这样导入模块并使用(假设你在 module1 和 module2 中分别实现了函数 func1 和 func2 ):

    # main.py
    import package.module1
    import package.subpackage.module2
    
    package.module1.func1()
    package.subpackage.module2.func2()

    这样很麻烦对不对?前缀死长死长的。 

    可以使用绝对路径直接导入包中的对象:

    # main.py
    from package.module1 import func1
    from package.subpackage.module2 import func2
    func1()
    func2()

    但是这样导入,如果包的层次很多怎么办,那导入语句就会很长?所以接下来讲讲 __init__.py 怎么用,这才是包的作用!【本质其实有点像是超链接】搞完后面的操作你就可以像下面这样导入并调用包里的函数了:

    # main.py
    from package import func1, func2
    func1()
    func2()

    只需要在两个 __init__.py 文件中分别增加以下内容就可以了!

    # package/__init__.py
    from .module1 import func1
    # package/subpackage/__init__.py
    from .module2 import func2

    如果你直接执行这些__init__.py文件,则会报错:ImportError: attempted relative import with no known parent package  这是正常的。

    在包内部的模块之间可以使用相对导入。

    示例:module1.py 中导入 subpackage/module2.py 中的 func2

    from .subpackage.module2 import func2
    • . 表示当前包。
    • .. 表示上一级包。

    注意:

    • 相对导入只能在包内部使用,不能在包外部使用。
    • 直接运行使用相对导入的 .py 文件(脚本)会报错!!!!

    如果导入的对象与当前命名空间中的对象同名,会导致冲突。可以通过别名解决。

    示例:

    from math import sqrt
    from mymodule import sqrt as my_sqrt
    
    print(sqrt(16))    # 使用 math 模块中的 sqrt
    print(my_sqrt(16)) # 使用 mymodule 模块中的 sqrt

    使用 import 语句导入模块时,python如何识别导入的模块时哪一个?

    Python 会按照以下顺序查找模块:

    • 当前工作目录(os.getcwd())。
    • PYTHONPATH 环境变量。
    • 安装的 Python 包和模块(标准库和第三方库)。

    如果模块不在这些路径中,可以通过以下方式解决:

    将模块目录添加到 sys.path

    import sys
    sys.path.append('/path/to/module/directory')

    修改 PYTHONPATH 环境变量。

    如果两个模块相互导入对方,可能会导致循环依赖问题。尽量避免这种情况,或者通过调整模块结构来解决。

    ps:我之前没搞明白包和模块啊这些东西的概念,乱用一通,导致项目老是报错,特别是乱七八糟的导入引用,VSCode打开的文件夹层级变一下也会导致原本正常的程序报错。。。一开始我直接在浏览器搜,看了几篇文章感觉讲得乱七八糟的看不懂,最后还是求助KIMI老师,然后自己实践了一遍,终于是大概明白之前为啥老是报错了(就文中提到的ImportError: attempted relative import with no known parent packageAttributeError: module 'package' has no attribute 'module1' )。

    到此这篇关于python中相对路径导入的正确用法及错误避免方法的文章就介绍到这了,更多相关python相对路径导入内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • Python导入自定义路径的方法
    • 浅谈Python2、Python3相对路径、绝对路径导入方法
    • python根据路径导入模块的方法
    • python中使用(.)进行相对路径访问文件的操作方法
    • Python多模块引用由此引发的相对路径混乱问题
    • python调用文件时找不到相对路径的解决方案
    • Python如何读取相对路径文件
    • 理解Python中的绝对路径和相对路径

    站内搜索