
本文深入探讨了如何利用 Python tqdm 库为文件处理和写入操作添加进度条。不同于常见的下载进度追踪,我们将展示一种策略,通过监控文件级别的处理完成情况来更新进度条,特别适用于一次性读取和写入整个文件内容的场景。文章将提供详细的代码示例和实现步骤,帮助开发者在文件加密、转换等任务中实现直观的进度反馈。
tqdm 库简介与常见应用
tqdm 是一个功能强大且易于使用的 python 库,用于在循环迭代过程中显示进度条。它的名字来源于阿拉伯语 taqaddum (تقدم) 意为“进步”,也恰好是“我很快乐” (i’m happy) 的缩写。tqdm 最常见的应用场景是文件下载、数据处理或任何涉及迭代大量任务的场景。
例如,在文件下载过程中,tqdm 可以通过迭代数据块并更新已接收的字节数来实时显示下载进度:
import requestsfrom tqdm import tqdmurl = 'https://wordnetcode.princeton.edu/2.1/WNsnsmap-2.1.tar.gz'filename = url.split('/')[-1]resp = requests.get(url, stream=True)# 初始化进度条,total 为文件总大小,unit='B' 表示单位是字节pbar = tqdm(desc=filename, total=int(resp.headers.get('content-length', 0)), unit='B', unit_scale=True, unit_divisor=1024)with open(filename, 'wb') as f: for data in resp.iter_content(chunk_size=1024): # 按块迭代内容 f.write(data) pbar.update(len(data)) # 每次写入一块数据后更新进度条pbar.close() # 关闭进度条
在这个例子中,iter_content(chunk_size=1024) 使得数据以小块的形式流式传输,每次写入一块后,我们都可以通过 pbar.update(len(data)) 来精确更新已写入的字节数,从而实现实时的进度反馈。
文件处理场景下的挑战
然而,当面对本地文件处理任务,例如读取整个文件内容进行加密或转换,然后一次性写入处理后的数据时,传统的按块更新进度条的方法就不那么直接适用了。
考虑以下场景:
立即学习“Python免费学习笔记(深入)”;
import osfrom base64 import b85encode # 假设 b85encode 是你的处理逻辑input_dir = "path/to/your/files" # 替换为你的输入目录for filefolder, dirs, files in os.walk(input_dir): for filename in files: file_path = os.path.join(filefolder, filename) print(f"正在加密 {filename}..") try: with open(file_path, 'rb') as encryptingfile: # 一次性读取整个文件内容 original_bytes = encryptingfile.read() # 进行加密处理,例如 Base85 编码 b85_bytes = b85encode(original_bytes) # 一次性写入处理后的所有字节 with open(file_path, 'wb') as output_file: output_file.write(b85_bytes) # 问题:在哪里调用 pbar.update() 来显示 output_file.write(b85_bytes) 的进度? # 因为 write() 是一个原子操作,它不提供内部的字节写入进度。 except PermissionError: print(f"r跳过: {filename} (权限不足)n") except Exception as e: print(f"r处理 {filename} 时发生错误: {e}n")
在这个代码块中,encryptingfile.read() 一次性将整个文件读入内存,b85encode() 对整个内容进行处理,而 output_file.write(b85_bytes) 也是一次性将所有处理后的字节写入文件。write() 方法本身不提供分块写入的接口,因此我们无法像下载示例那样,在每次写入一小块数据后更新进度条。我们需要一种不同的策略来追踪这类文件处理任务的进度。
解决方案:基于文件完成度的进度追踪
对于上述挑战,一种有效的解决方案是:将进度条的更新粒度从“字节块”提升到“文件完成”。也就是说,我们不追踪单个文件内部的字节写入进度,而是追踪整个文件集合中每个文件处理完成的进度。当一个文件被完整处理并写入完成后,我们就更新进度条,增加该文件的大小到已处理总量中。
这种方法的核心思想是:
首先计算所有待处理文件的总大小,作为 tqdm 进度条的 total 值。在循环处理每个文件时,当该文件的所有操作(读取、处理、写入)完成后,通知 tqdm 该文件已处理完毕。
下面是具体的实现步骤和代码:
步骤一:计算总处理量
我们需要一个函数来遍历指定文件夹下的所有文件,并计算它们的总大小。这将作为 tqdm 进度条的 total 参数。
import osfrom tqdm import tqdmimport time # 用于模拟文件处理耗时from base64 import b85encode, b85decode # 模拟加密/解密def iter_files(folder_path): """ 遍历指定文件夹及其子文件夹中的所有文件,并返回文件大小和完整路径。 """ for root, _, files in os.walk(folder_path): for file_name in files: file_full_path = os.path.join(root, file_name) try: # 确保文件存在且可访问,获取其大小 if os.path.exists(file_full_path) and os.path.isfile(file_full_path): yield os.path.getsize(file_full_path), file_full_path except Exception as e: print(f"无法获取文件大小或访问文件: {file_full_path}, 错误: {e}") continue
iter_files 函数是一个生成器,它会逐个返回每个文件的大小和路径。通过 sum(s for s, _ in iter_files(folder_path)) 就可以方便地计算出所有文件的总大小。
步骤二:封装进度更新逻辑
接下来,我们创建一个函数来初始化 tqdm 进度条,并为每个文件提供一个回调函数,当文件处理完成后调用此回调来更新进度。
def iter_with_progress(folder_path, description="处理文件"): """ 初始化一个 tqdm 进度条,并为每个文件提供一个更新进度的回调函数。 """ # 计算所有文件的总大小作为进度条的总量 total_size = sum(s for s, _ in iter_files(folder_path)) # 初始化 tqdm 进度条 progress_bar = tqdm(unit='B', total=total_size, unit_scale=True, unit_divisor=1024, desc=description) # 遍历文件,并为每个文件提供一个更新进度的回调 for file_size, file_path in iter_files(folder_path): # 使用 lambda 表达式创建一个闭包,当文件处理完成后调用它来更新进度条 update_callback = lambda size=file_size: progress_bar.update(size) yield update_callback, file_size, file_path # 在所有文件迭代完毕后,确保进度条关闭 # 注意:如果循环提前终止,可能需要手动调用 progress_bar.close() progress_bar.close()
iter_with_progress 也是一个生成器。它首先计算出所有文件的总大小并初始化 tqdm 实例。然后,在每次迭代中,它会 yield 一个元组,其中包含:
update_callback: 一个函数,调用它即可将当前文件的大小添加到进度条的已完成量中。file_size: 当前文件的大小。file_path: 当前文件的完整路径。
步骤三:集成到文件处理流程
现在,我们可以将上述函数集成到实际的文件处理(例如加密/解密)流程中:
# 示例:模拟文件加密并更新进度条# 请将 'path/to/your/input/directory' 替换为你要处理的文件夹路径input_directory = 'path/to/your/input/directory'# 确保输入目录存在if not os.path.isdir(input_directory): print(f"错误:目录 '{input_directory}' 不存在。请替换为有效路径。")else: print(f"开始处理目录: {input_directory}") for update_progress_callback, file_size, file_path in iter_with_progress(input_directory, description="文件处理"): try: print(f"n正在处理文件: {os.path.basename(file_path)} ({file_size} Bytes)") # 模拟文件读取和加密操作 with open(file_path, 'rb') as f_read: original_bytes = f_read.read() # 假设 b85encode 是你的加密逻辑 processed_bytes = b85encode(original_bytes) # 或 b85decode(original_bytes) 进行解密 # 模拟文件写入操作 # 注意:这里直接覆盖原文件,实际应用中可能写入新文件或进行备份 with open(file_path, 'wb') as f_write: f_write.write(processed_bytes) # 文件处理完成后,调用回调函数更新进度条 update_progress_callback() except PermissionError: print(f"跳过文件 (权限不足): {os.path.basename(file_path)}") except Exception as e: print(f"处理文件 {os.path.basename(file_path)} 时发生错误: {e}") print("n所有文件处理完毕。")
在这个集成示例中,for 循环从 iter_with_progress 中获取每个文件的信息和更新回调。在 try 块中,我们执行实际的文件读取、处理(例如 b85encode)和写入操作。一旦这些操作成功完成,我们就调用 update_progress_callback(),这将通知 tqdm 进度条当前文件已处理完成,并更新已完成的总字节数。
tqdm 参数详解
在上面的示例中,我们使用了 tqdm 的几个关键参数:
unit=’B’: 指定进度条的单位为字节(Bytes)。total=total_size: 设置进度条的总量,这里是所有待处理文件的总大小。unit_scale=True: 启用单位自动缩放,例如,当字节数达到 KB、MB、GB 时,会自动显示相应的单位。unit_divisor=1024: 设置单位缩放的除数,通常是 1024 (KB, MB) 或 1000 (千, 百万)。对于文件大小,1024 更常见。desc=”文件处理”: 设置进度条的描述信息,会显示在进度条的前面。
注意事项与进阶思考
适用场景与局限性:
此方法非常适合处理文件夹中多个文件的场景,它能清晰地展示整个批处理任务的进度。局限性: 对于单个文件非常大,且其内部处理(如加密)本身耗时很长的情况,此方法在文件处理完成前不会更新进度条。如果需要追踪单个大文件内部的实时进度,则需要将文件内容分块读取、处理和写入,并在每次处理完一个块后更新 tqdm。例如,可以使用 functools.partial 或自定义包装器来包装 file.write,使其在每次写入时报告进度。
内存管理:
在示例中,我们使用了 encryptingfile.read() 一次性读取整个文件内容到内存。对于非常大的文件(例如几 GB 甚至更大),这可能导致内存溢出。建议: 对于大文件,应考虑采用分块读写的方式,即使是加密/解密操作,也可以设计为流式处理。例如,读取一个块,处理一个块,写入一个块,这样可以显著降低内存消耗。
错误处理:
在实际应用中,务必加入健壮的错误处理机制。示例中包含了 PermissionError 和通用的 Exception 捕获,但在生产环境中,可能需要更细致的错误类型判断和日志记录。
进度条的关闭:
tqdm 实例在 total 达到后会自动关闭。在 iter_with_progress 函数中,我们显式调用了 progress_bar.close() 来确保在生成器迭代完毕后,进度条资源被正确释放。
总结
通过本文的介绍,我们学习了如何利用 tqdm 库在 Python 中为文件处理和写入操作添加直观的进度条。与传统的下载进度追踪不同,我们采用了一种基于文件完成度的策略,通过计算所有待处理文件的总大小,并在每个文件处理完成后更新进度条,从而有效解决了 file.write() 操作原子性带来的进度追踪难题。掌握这种方法,将有助于你在开发文件批量处理工具时提供更好的用户体验。
以上就是Python tqdm 实践:构建文件处理与写入操作的进度条的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1363381.html
微信扫一扫
支付宝扫一扫