importlib模块

一、动态导入**import_module**

语法:

复制代码
importlib.import_module(name, package=None)

name (必填):要导入的模块名称,可以是绝对名称(如 'os.path'),也可以是相对名称(以点开头,如 '.submodule')。

package (选填):仅当 name 是相对名称时必须提供,用于指定"锚点"包。通常传入当前模块的 __package__ 属性(或者当前包的名字)。

返回值:成功则返回导入的模块对象,失败则抛出 ImportError

示例代码(一)

module/info.py

python 复制代码
def student(name,age):
    print(f"姓名:{name}\n年龄:{age}岁")
if __name__ == '__main__':
    student("张三一",26)

01import_module.py

python 复制代码
import importlib
info1=importlib.import_module(name="module.info")
# 切记参数name=".info",info前有一个点
info2 = importlib.import_module(name=".info", package="module")
"""
info1与info2等价
info1中name="module.info"写的是绝对路径,因此不需要传入package参数
而
info2中name=".info"传入的是相对路径,因此需要传入package参数
"""
info1.student("张三一",26)
print("-"*19)
info2.student("张三一",26)

01import_module.py运行结果:

python 复制代码
姓名:张三一
年龄:26岁
-------------------
姓名:张三一
年龄:26岁

示例代码(二):

module/company.py

python 复制代码
class worker():
    def name(self):
        print("姓名:李靖")
    def age(self):
        print("年龄:66岁")
    def address(self):
        print("地址:大唐长安")

02importlib_module2.py

python 复制代码
import importlib
company=importlib.import_module(name="module.company")
worker=company.worker()
# 下面输入worker.后,函数名称是可以自动提示的
worker.name()
worker.age()
worker.address()

02importlib_module2.py运行结果

python 复制代码
C:\Users\asus\anaconda3\python.exe "D:\My Program\study_project\06importlib\01.py" 
姓名:李靖
年龄:66岁
地址:大唐长安

进程已结束,退出代码0

二、 模块重载reload

语法:

复制代码
import importlib
importlib.reload(module)

参数:必须是一个已经导入过的模块对象(不能是字符串,也不能是未导入的模块)。

返回值:返回重新加载后的模块对象(其实就是传进去的那个对象,但在内存中被刷新了)。

作用:强制 Python 重新执行该模块的顶层代码,并用新的内容更新 sys.modules 缓存。

示例代码:

module/calc.py

python 复制代码
# calc.py
x = 10
def add(a, b):
    return a + b

03reload.py

python 复制代码
import importlib
import time

from module import calc

print(calc.x)      # 输出 10
print(calc.add(2, 3))  # 输出 5


for i in range(10):
    time.sleep(1)
    print(f"等待{i + 1}秒...")
# ---在这个等待的时间,如果修改了 calc.py,把 x 改成 20,add 改成 a - b ---,并按Ctrl+S保存module/calc.py
# 修改后,不重启 Python,直接调用 reload
importlib.reload(calc)  # 重新加载

print(calc.x)      # 输出 20 ✅
print(calc.add(2, 3))  # 输出 -1 ✅

03reload.py运行结果

python 复制代码
C:\Users\asus\anaconda3\python.exe "D:\My Program\study_project\06importlib\03reload.py" 
10
5
等待1秒...
等待2秒...
等待3秒...
等待4秒...
等待5秒...
等待6秒...
等待7秒...
等待8秒...
等待9秒...
等待10秒...
20
-1

进程已结束,退出代码0

三、现代化资源管理resources(⭐⭐⭐⭐⭐ 重点)

(一)为什么要用它?(解决两大噩梦)

  • 告别 __file__ 拼路径:不会因为打包成 EXE 或运行在临时目录而找不到文件。

  • 告别手动复制资源:打包时自动将文件嵌入 EXE 内部,无需手动拷贝。

(二)核心前提(第一个坑:__init__.py

铁律:被读取的文件夹,必须是一个"Python 包",而不是普通文件夹。

错误结构 (导致 TypeError: ... NoneType):

复制代码
loadfile/          <-- 只是个普通文件夹,没有 __init__.py
└── config.txt

正确结构(必须加空文件)

复制代码
loadfile/          <-- 这是一个"包"
├── __init__.py    <-- 必须存在(可以为空)
└── config.txt

(三)读取文件 API(区分大小和场景)

1. 小文件(几 KB ~ 几 MB)------ 一次性读取
  • 读取文本files("包名").joinpath("文件名").read_text(encoding="utf-8")

  • 读取二进制files("包名").joinpath("文件名").read_bytes()

2. 大文件(避免内存溢出)------ 流式/分批读取(第二个坑)

不能用 read_text(),改用 .open() 配合循环。

python 复制代码
from importlib.resources import files

resource = files("loadfile").joinpath("big_log.txt")

# 方式一:按行读取(最省内存,适合文本)
with resource.open('r', encoding='utf-8') as f:
    for line in f:
        process(line)  # 逐行处理

# 方式二:按字节块读取(适合二进制或超大文件)
with resource.open('rb') as f:
    while chunk := f.read(4096):  # 每次读 4KB
        process(chunk)
3. 第三方库需要"真实路径"时 ------ 使用 as_file()

如果 pandas.read_csv()PIL.Image.open() 不接受字节流,只认文件路径,用这个:

python 复制代码
from importlib.resources import files, as_file

resource = files("loadfile").joinpath("data.csv")
with as_file(resource) as path:   # 自动生成临时真实文件路径
    df = pd.read_csv(path)        # 完美兼容

(四)打包部署(解决手动复制文件的噩梦)

前提 :你的代码必须使用 importlib.resources,而不是 __file__

1. 用 PyInstaller 打包成单个 EXE(命令)

在终端运行:

python 复制代码
# Windows 系统(注意分号 ;)
pyinstaller -F --add-data "loadfile;loadfile" 04.py

# Mac/Linux 系统(注意冒号 :)
pyinstaller -F --add-data "loadfile:loadfile" 04.py

-F:打包成单文件 EXE。

--add-data "本地文件夹;打包后文件夹":将整个资源包打进 EXE 内部。

2. 使用 .spec 文件(一劳永逸)

如果资源很多,每次敲命令太麻烦,生成一个配置文件:

python 复制代码
pyi-makespec 04.py

然后在生成的 04.spec 文件中找到 datas 列表,添加上你的资源包:

python 复制代码
a = Analysis(
    ['04.py'],
    datas=[('loadfile', 'loadfile'), ('images', 'images')],  # 自动打包所有资源
    ...
)

以后只需要运行 pyinstaller 04.spec 即可,无需反复敲参数。

(五)重要提醒:关于"写入"文件(第三个坑)

importlib.resources 只能"读取",不能"写入"。EXE 内部的资源文件是只读的。

  • 正确做法 :程序首次运行时,用 resources 读取默认配置,然后用普通的 open()当前用户目录 (如 os.getcwd()AppData)下生成副本。以后修改和写入都针对副本操作。

  • 这样既保留了原版配置,又支持用户自定义修改。

(六)常见错误清单(帮你快速排雷)

错误现象 根本原因 解决方案
TypeError: expected str, bytes ... not NoneType 资源文件夹 没有 __init__.py 在文件夹里新建一个空的 __init__.py
读取大文件时内存爆满 / 程序卡死 错误使用了 read_text() 一次性读入 改用 .open() + for line in f 逐行读取
打包后 EXE 运行报错"文件找不到" 代码用了 __file__ 拼接路径 换成 importlib.resources 写法
打包后资源文件不存在 打包时忘了加 --add-data 参数 打包命令加上 --add-data "包名;包名"
as_file() 报错或无法释放 忘记用 with 上下文管理器 必须写成 with as_file(resource) as path:

(七)总结(你的"心法口诀")

资源文件夹必须加 __init__.py,这是包的身份证。

小文件 read_text,大文件 open 循环。

传给第三方库用 as_file,打包时带上 --add-data

只读不写,改动副本,内存安全,打包无忧。

四、查找器(Finder)与加载器(Loader)

五、元数据读取metadata(姊妹模块)

相关推荐
axinawang1 小时前
第25课 for循环的应用
python
猫猫不是喵喵.2 小时前
Vue2 的 Vuex 状态管理与 Vue3 的 Pinia 状态管理
前端·javascript·vue.js
逻极2 小时前
FastAPI 实战:从入门到自动化文档,如何把API开发效率提升200%
python·api·fastapi·swagger·异步
greenbbLV2 小时前
中小公司积分商城选型:SaaS与私有化方案对比解析
大数据·前端·小程序
techdashen2 小时前
Go设计取舍之四: map不变时能否并发修改不同value
开发语言·后端·golang
小小小米粒2 小时前
阿姆达尔定律(Amdahl‘s Law)
java·开发语言
知彼解己2 小时前
Java 版本演进
java·开发语言·spring boot
风样滴男人哟2 小时前
PHP特性之反射类ReflectionClass机制
android·开发语言·php
半兽先生2 小时前
大模型技术开发与应用——5.大模型Agent开发(CrewAI)
大数据·人工智能·python·机器学习·ai