Python 操作 Excel 高阶技巧:用 openpyxl 玩转循环与 Decimal 精度控制

Python 操作 Excel 高阶技巧:用 openpyxl 玩转循环与 Decimal 精度控制

目录

专栏导读

❤️ 欢迎各位佬关注! ❤️
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
📕 此外还有python基础专栏:请点击——>Python基础学习专栏 求订阅
🕷 此外还有爬虫专栏:请点击——>Python爬虫基础专栏 求订阅
👍 该系列文章专栏:请点击——>Python办公自动化专栏 求订阅
🏳️‍🌈 ZEEKLOG博客主页:请点击——> ZEEKLOG的博客主页 求关注
🏳️‍🌈 知乎主页:请点击——> 知乎主页 求关注
🏳️‍🌈 Github主页:请点击——> Github主页 求Star⭐
🏳️‍🌈 个人博客主页:请点击——> 个人的博客主页 求收藏
🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手

Python 操作 Excel 高阶技巧:用 openpyxl 玩转循环与 Decimal 精度控制

一、 为什么你的 Excel 数据处理总是“差一点”?

在 Python 自动化办公的场景中,openpyxl 库无疑是操作 Excel 文件的首选利器。它不仅能读写数据,还能控制样式、图表和公式。然而,很多初学者在处理大规模数据或涉及金额计算的报表时,往往会遇到两个棘手的问题:

  1. 性能瓶颈:当需要遍历几万行数据进行格式化或计算时,简单的 for 循环写法可能导致程序运行极其缓慢,甚至内存溢出。
  2. 精度丢失:在处理财务数据时,浮点数运算的“舍入误差”是绝对不能容忍的(例如 0.1 + 0.2 != 0.3)。直接将浮点数写入 Excel 往往会引发业务逻辑错误。

本篇文章将深入探讨如何结合 Python 的 decimal 模块与 openpyxl 的高效循环技巧,打造一个既精准高效的数据处理脚本。


二、 精度之痛:用 Decimal 拯救你的财务数据

在处理金额、税率或任何对精度要求极高的数据时,使用 Python 原生的 float 类型是一场灾难。

1. 浮点数的“陷阱”

Python 的 float 遵循 IEEE 754 标准,这导致了二进制无法精确表示某些十进制小数。

# 经典的浮点数问题print(0.1+0.2)# 输出: 0.30000000000000004

当你把这个结果写入 Excel 时,虽然 Excel 自身也有精度限制,但在数据传输阶段就已经埋下了隐患。

2. Decimal 模块的引入

Python 的 decimal 模块提供了一种十进制浮点运算,它能够完全模拟人工计算的逻辑。

实战技巧:在写入 Excel 前进行转换

在使用 openpyxl 写入单元格时,我们需要确保数据类型是精确的。

from decimal import Decimal, getcontext # 设置精度(可选,视业务需求而定) getcontext().prec =4# 模拟业务数据 value_a = Decimal('0.1') value_b = Decimal('0.2') result = value_a + value_b # 结果精确为 Decimal('0.3')# 在写入 openpyxl 时,可以直接写入 Decimal 对象# openpyxl 会自动将其转换为浮点数,但为了保险,建议转为 float ws.cell(row=1, column=1, value=float(result))

核心建议

  • 计算阶段:全程使用 Decimal 对象进行加减乘除。
  • 写入阶段:将 Decimal 结果转换为 float 再赋值给 ws.cell(),或者直接赋值(openpyxl 会处理),但务必在计算过程中避免混合使用 floatDecimal

三、 效率革命:Openpyxl 的高效循环策略

当你需要处理包含成千上万行数据的 Excel 文件时,低效的循环写法会让你的 CPU 占用率飙升。

1. 最慢的写法:逐行写入并保存

这是一个典型的错误示范:

# ❌ 性能杀手:在循环中反复保存或频繁操作单元格对象for row inrange(1,10000):for col inrange(1,10):# 每次调用 ws.cell 都有一定开销 ws.cell(row=row, column=col, value=row * col) wb.save('slow_file.xlsx')

这种做法不仅慢,而且如果文件很大,很容易导致内存问题。

2. 进阶写法:使用 append() 批量写入

如果你是按行顺序写入数据,append() 方法比逐个 cell() 赋值要快得多。

# ✅ 推荐:按行追加数据import time from decimal import Decimal data_source =[[Decimal('100.50'), Decimal('200.30')],[Decimal('101.00'), Decimal('202.00')],# ... 假设这里有成千上万行]for row_data in data_source:# 将 Decimal 转换为 float 或直接写入 ws.append([float(x)for x in row_data])

3. 高阶写法:内存优化与公式填充

在处理超大数据量时,如果必须逐个单元格赋值(例如需要根据上一行计算下一行),可以使用以下技巧:

  • 关闭自动计算:Excel 打开时会自动重算公式,如果数据量大,建议先写入数据,最后再写入公式,或者在 Python 中计算好结果直接写入值。
  • 利用生成器(Generator):不要一次性把所有数据加载到列表中,使用生成器流式处理数据,减少内存占用。
# 假设 data_generator 是一个生成器,源源不断地产生数据defdata_generator():for i inrange(1,100000):yield[Decimal(i)* Decimal('1.05'), Decimal(i)* Decimal('0.95')]# 流式写入for row_idx, row_data inenumerate(data_generator(),1):# 这里的逻辑比较复杂,因为 openpyxl 的 append 是最快的# 如果必须使用 cell 赋值,请注意减少属性访问次数 ws.cell(row=row_idx, column=1, value=float(row_data[0])) ws.cell(row=row_idx, column=2, value=float(row_data[1]))

4. 终极加速:只读模式与公式缓存

如果你需要读取 A 列,计算后写入 B 列,不要在循环中反复读取单元格。

# ❌ 慢for row inrange(1, ws.max_row +1): val = ws.cell(row=row, column=1).value ws.cell(row=row, column=2, value=val *2)# ✅ 快 (先批量读取到内存,再批量计算,最后写入)# 但对于超大文件,这会撑爆内存,所以折中方案是:# 1. 将 ws.max_row 分段处理# 2. 或者使用 openpyxl 的 read_only 模式读取,计算,然后用 write_only 模式写入新文件。

最佳实践:read_onlywrite_only 模式
这是处理超大 Excel 文件(如 50MB+)的必杀技。

from openpyxl import load_workbook, Workbook # 1. 以只读模式加载源文件(极低内存占用) wb_read = load_workbook('big_data.xlsx', read_only=True) ws_read = wb_read.active # 2. 创建新工作簿(或以 write_only 模式保存) wb_write = Workbook(write_only=True) ws_write = wb_write.create_sheet()# 3. 循环处理# read_only 模式下,只能使用 ws.iter_rows() 遍历for row in ws_read.iter_rows(values_only=True):# row 是一个元组,包含该行的所有值# 这里进行 Decimal 计算if row[0]isnotNone: val_a = Decimal(str(row[0]))# 转换为 Decimal val_b = val_a * Decimal('1.1')# write_only 模式下,只能使用 append 写入 ws_write.append([float(val_b)])# 4. 保存 wb_write.save('processed_big_data.xlsx')

这种模式下,内存占用极低,因为数据是流式读取和写入的,不会一次性加载到内存中。


四、 综合实战:构建一个高精度报表生成器

让我们把上述知识点结合起来,编写一个完整的脚本。场景:处理一份包含大量交易记录的 CSV(模拟),计算税费,并写入 Excel,要求金额精确,且处理速度快。

import csv from decimal import Decimal, ROUND_HALF_UP from openpyxl import Workbook from openpyxl.styles import Font, Alignment # 模拟生成一个大 CSV 文件(实际中可能是读取外部文件)defgenerate_mock_csv(filename, rows=50000):withopen(filename,'w', newline='')as f: writer = csv.writer(f) writer.writerow(['ID','Amount','TaxRate'])for i inrange(1, rows +1): writer.writerow([i,f"{(i %100)+100}.50","0.08"])defprocess_financial_report(input_csv, output_xlsx):# 1. 初始化工作簿 wb = Workbook(write_only=True) ws = wb.create_sheet()# 2. 写入表头 headers =['ID','原始金额','税率','税额','总金额'] ws.append(headers)# 3. 设置 Decimal 上下文# ROUND_HALF_UP: 四舍五入,0.5 向上进位 Decimal('0.01').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)# 4. 读取 CSV 并计算 (流式处理)withopen(input_csv,'r')as f: reader = csv.reader(f)next(reader)# 跳过表头 batch_data =[]# 缓冲区,批量写入可略微提升性能,但 write_only 模式下 append 已经很快for row in reader:ifnot row:continue raw_id =int(row[0]) raw_amount = Decimal(row[1]) tax_rate = Decimal(row[2])# 计算逻辑 (Decimal 精度) tax_amount =(raw_amount * tax_rate).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP) total_amount = raw_amount + tax_amount # 准备写入数据 (转换为 float 或保留 Decimal)# openpyxl 支持写入 Decimal,但为了显式控制,我们转为 float# 注意:如果 Excel 仅用于展示,float 足够;若需二次计算,建议转为字符串或保留 Decimal# 这里我们转为 float 展示 row_data =[ raw_id,float(raw_amount),float(tax_rate),float(tax_amount),float(total_amount)] ws.append(row_data)# 简单的进度提示(实际生产中可使用 tqdm)if raw_id %10000==0:print(f"已处理 {raw_id} 行数据...")# 5. 保存文件print(f"正在保存文件: {output_xlsx}") wb.save(output_xlsx)print("完成!")# 执行演示if __name__ =="__main__": input_csv ='mock_transactions.csv' output_xlsx ='financial_report.xlsx'# 生成测试数据print("正在生成模拟数据...") generate_mock_csv(input_csv, rows=50000)# 处理数据 process_financial_report(input_csv, output_xlsx)

代码解析:

  1. Decimal 的使用:在读取字符串转为 Decimal 时,使用 Decimal(row[1]) 而非 float(row[1]),彻底杜绝精度误差。
  2. quantize 方法:这是控制小数位数和舍入方式的关键。例如 .quantize(Decimal('0.00')) 强制保留两位小数。
  3. write_only 模式:在创建 Workbook 时开启,配合 ws.append(),即使处理 50,000 行数据也能秒级完成,且内存占用极低。
  4. 流式读取 CSV:使用 csv 模块逐行读取,不一次性加载大文件到内存。

五、 总结与避坑指南

在 Python 中使用 openpyxl 结合 Decimal 进行数据处理,是企业级开发的标准实践。总结一下核心要点:

  1. 数据精度优先:凡是涉及金额、统计、科学计算,务必使用 Decimal 类型,仅在最终展示或写入 Excel 的瞬间转换为 float
  2. 选择正确的读写模式
    • 小文件(<10MB):常规模式,随意操作。
    • 大文件(>10MB 或 >10万行):必须使用 read_only=True 读取,write_only=True 写入。
  3. 避免混合运算:不要让 Decimalfloat 在同一个公式里混用,这会触发隐式转换,导致精度丢失。

通过以上技巧,你可以轻松应对绝大多数 Excel 数据处理任务,写出既健壮又高效的代码。

互动环节:
你在使用 Python 处理 Excel 数据时,还遇到过哪些奇葩的“坑”?是日期格式转换乱码,还是合并单元格后数据读取错位?欢迎在评论区分享你的经历,我们一起探讨解决方案!

结尾

此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
希望能得到大家的【❤️一个免费关注❤️】感谢!
希望对初学者有帮助;致力于办公自动化的小小程序员一枚

Read more

❿⁄₁₃ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 获取并破解Net-NTLMv2哈希(下)

❿⁄₁₃ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 获取并破解Net-NTLMv2哈希(下)

郑重声明:本文所涉安全技术仅限用于合法研究与学习目的,严禁任何形式的非法利用。因不当使用所导致的一切法律与经济责任,本人概不负责。任何形式的转载均须明确标注原文出处,且不得用于商业目的。 🔋 点赞 | 能量注入 ❤️ 关注 | 信号锁定 🔔 收藏 | 数据归档 ⭐️ 评论 | 保持连接💬 🌌 立即前往 👉晖度丨安全视界🚀 ▶ 信息收集  ▶ 漏洞检测 ▶ 初始立足点  ▶ 权限提升 ▶ 横向移动 ➢ 密码攻击 ➢  获取并破解Net-NTLMv2哈希(下)🔥🔥🔥 ▶ 报告/分析 ▶ 教训/修复 目录 1.密码破解 1.1 破解Windows哈希实践 1.1.3 捕获Net-NTLMv2哈希实践 1.1.3.3 使用Netcat连接绑定 Shell(kali上) 1.连接流程 2.连接命令

By Ne0inhk
《算法题讲解指南:优选算法-模拟》--41.外观数列,42.数青蛙

《算法题讲解指南:优选算法-模拟》--41.外观数列,42.数青蛙

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 41.外观数列 题目链接: 题目描述: 题目示例: 解法(模拟): 算法思路: C++算法代码: 算法总结及流程解析: 42.数青蛙 题目链接: 题目描述: 题目示例: 解法(模拟+分情况讨论): 算法思路: C++算法代码: 算法总结及流程解析: 结束语 41.外观数列 题目链接: 38. 外观数列 - 力扣(LeetCode) 题目描述: 题目示例:

By Ne0inhk
链表与LinkedList

链表与LinkedList

前言 来啦来啦~ 今天和大家分享链表与LinkedList的内容,结构差不多,如果大家有了顺序表的基础接受到这一部分会更加容易,我们还是集合框架出发,开始吧 一、java集合框架 * Java 集合框架是 Java 中用于存储和操作一组对象的体系,核心分为 Collection(单列集合)和Map(双列集合) 核心接口与分类 * Collection(单列集合) * 是所有单列集合的根接口,定义了集合的基本操作(增删改查、遍历等)。 * 子接口:List(有序可重复)、Set(无序不可重复)、Queue(队列)。 * Map(双列集合) * 存储键值对(Key-Value),Key 唯一、Value 可重复。 * 子接口:SortedMap(键有序)。 * 咱今天就接着看LinkedList. LinkedList 1. 实现的接口 * 实现了List接口(具备列表的增删改查能力); * 实现了Deque接口(

By Ne0inhk
模拟实现B-树详解

模拟实现B-树详解

目录 B-树 定义 特性 B-树的插入分析 B-树插入总结 模拟实现B-树 基本结构  寻找插入位置  插入元素 分裂节点  中序遍历 完整代码  代码测试 B-树的删除 B-树的优点 B-树的应用场景 B+树 B+树的优势 B+树的应用场景 B+树与B树的区别 B*树 特点 B*树的优势 总结 B-树 定义 B-树是一种平衡的M(M>=2)路查找树,B-树也可以是空树,每个节点可以拥有多个子节点,从而有效减少树的高度,提高查找效率。 特性 1. 根节点至少有两个孩子; 2. 每个非根节点至少有M/2-1(上取整)个关键字,

By Ne0inhk