从 ModuleNotFoundError 讲 Python 的模块与包

本文详细介绍了Python模块和包的导入技巧,包括模块搜索路径、正确引入其他模块的方法、包内模块间导入以及解决`ModuleNotFoundError`的策略。实例演示了如何在主目录和包结构中导入模块,并强调了相对导入的适用范围和限制。
摘要由CSDN通过智能技术生成

目录

1 了解Python中的模块与包

2 如何正确引入模块

2.1 模块搜索路径

2.2 实践说明

2.2.1 main.py如何导入其他模块

2.2.2 包的模块如何导入同个包中的其他模块(以dir2包为例)

2.2.3 包的模块如何导入其他包中的模块(以dir1包为例)

2.2.4 关于包的模块如何导入最外层的模块(以dir1包为例)

2.3 其他

3 参考文档


如果你还经常遇到 "ModuleNotFoundError: No module named" 这样的问题,那么可以稍微花几分钟看下这篇文章。

1 了解Python中的模块与包

模块module:其实就是一个个 python 文件。是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
包package:其实就是一个个目录,是一种 python 文件的代码组织。是一种用“点式模块名”构造 python 模块命名空间的方法。

举例说明:

下面所列的所有.py文件都是模块;main.py是这个Python工程的执行入口文件。

dir1跟dir2两个目录就是包。

|-- engzegngi/  
|   |-- dir1/
|   |   |-- __init__.py
|   |   |-- dir1_file1.py
|   |
|   |-- dir2/
|   |   |-- __init__.py
|   |   |-- dir2_file1.py
|   |   |-- dir2_file2.py
|   |
|   |-- main.py

2 如何正确引入模块

2.1 模块搜索路径

我们最常用的可能就是引入系统自带的模块,比如 import os 。Python是如何去搜索这些模块呢?那就要明白其“模块搜索路径”。官方定义如下图。

模块搜索路径

举例说明:

>>> import sys
>>> print(sys.path)
['', 
'/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python38.zip', 
'/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8', 
'/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/lib-dynload', 
'/Users/zeyiweng/.local/share/virtualenvs/gcloudsdk_autotest-wL5os1_R/lib/python3.8/site-packages']

 其中,

PYTHONPATH:就是打印出来的第二个到第四个路径。

依赖于安装的默认值:就是打印出来的最后一个路径。(题外话:如果新建python虚拟环境,其实就是新建了个site-packages目录,然后依赖库都是安装到这个目录下,以此达到环境隔离的目录。)

2.2 实践说明

以下述结构的工程为例(注意跟main.py同目录下有个config.py)。

|-- engzegngi/  
|   |-- dir1/
|   |   |-- __init__.py
|   |   |-- dir1_file1.py
|   |
|   |-- dir2/
|   |   |-- __init__.py
|   |   |-- dir2_file1.py
|   |   |-- dir2_file2.py
|   |
|   |-- config.py
|   |-- main.py

2.2.1 main.py如何导入其他模块

# main.py的所在目录为工程主目录engzegngi/
import config  # 从工程主目录engzegngi/下就可以搜索到
import dir1.dir1_file1  # 从工程主目录engzegngi/下就可以搜索到

2.2.2 包的模块如何导入同个包中的其他模块(以dir2包为例)

"""
当前在dir2_file1.py中,如何引用同个包内的dir2_file2.py
"""
# 正确示范
import dir2.dir2_file2  # 从工程主目录engzegngi/下就可以搜索到
from . import dir2_file2  # 相对导入,从当前包的路径engzegngi/dir2中导入

# 错误示范
# 报错ModuleNotFoundError: No module named 'dir2_file2'
# 因为从工程主目录engzegngi/下没办法搜索到
import dir2_file2

2.2.3 包的模块如何导入其他包中的模块(以dir1包为例)

"""
当前在dir1_file1.py中,如何引用dir2包内的dir2_file2.py
"""

import dir2.dir2_file2  # 从工程主目录engzegngi/下就可以搜索到

# 错误示范
# 报错ImportError: attempted relative import beyond top-level package
# 这里特别注意。容易以为可以使用相对导入。实际上不允许的。
# 因为dir1、dir2两个目录已经在顶层了。
from ..dir2 import dir2_file2

这里可以查看官方文档链接中的“6.4.2. 子包参考”这一部分:6. 模块 — Python 3.10.5 文档

相对路径导入是在包中的子包去导入兄弟包的模块的一种方法!对于已经都在工程最外层的包,比如这里的dir1、dir2,就不能使用相对导入了,因为这两个包不是兄弟包。

如果dir1中有两个目录dir3、dir4,那么这两者就可以使用相对路径导入其他包中的模块。有兴趣的朋友可以自己试下。

这里没搞清楚的话,是很容易犯错的。常见的报错有:

"""报错举例"""
ImportError: attempted relative import with no known parent package

ImportError: attempted relative import beyond top-level package

ValueError: attempted relative import beyond top-level package

ImportError: attempted relative import with no known parent package

2.2.4 关于包的模块如何导入最外层的模块(以dir1包为例)

import config  # 从工程主目录engzegngi/下就可以搜索到

2.3 其他

  • 模块可以区分不同模块之间的全局变量名称。在模块内部,通过全局变量  `__name__`  可以获取模块名(即字符串)。当以脚本方式运行某模块时,`__name__`  赋值为  `"__main__"`。
  • `from fibo import *` 会导入所有不以下划线( `_` )开头的名称。大多数情况下,不要用这个功能,这种方式向解释器导入了一批未知的名称,可能会覆盖已经定义的名称。`import fibo as fib` 模块名后使用  `as`  时,直接把  `as`  后的名称与导入模块绑定。
  • 从包中导入模块,例如`import sound.effects.echo`。一般不建议整个包导入。这项操作花费的时间较长,需要用哪些部分,就写清楚导入就好。

3 参考文档

1. https://docs.python.org/zh-cn/3/tutorial/modules.html
2. https://python3-cookbook.readthedocs.io/zh_CN/latest/chapters/p10_modules_and_packages.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值