tqdm 是一个功能强大且易于使用的 python 库,用于在循环迭代过程中显示进度条。它的名字来源于阿拉伯语 taqaddum (تقدم) 意为“进步”,也恰好是“我很快乐” (i'm happy) 的缩写。tqdm 最常见的应用场景是文件下载、数据处理或任何涉及迭代大量任务的场景。
例如,在文件下载过程中,tqdm 可以通过迭代数据块并更新已接收的字节数来实时显示下载进度:
import requests from tqdm import tqdm url = '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 os from 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 参数。
import os from tqdm import tqdm import 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 一个元组,其中包含:
现在,我们可以将上述函数集成到实际的文件处理(例如加密/解密)流程中:
# 示例:模拟文件加密并更新进度条 # 请将 '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 库在 Python 中为文件处理和写入操作添加直观的进度条。与传统的下载进度追踪不同,我们采用了一种基于文件完成度的策略,通过计算所有待处理文件的总大小,并在每个文件处理完成后更新进度条,从而有效解决了 file.write() 操作原子性带来的进度追踪难题。掌握这种方法,将有助于你在开发文件批量处理工具时提供更好的用户体验。
以上就是Python tqdm 实践:构建文件处理与写入操作的进度条的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号