Python使用openpyxl操作Excel的高阶技巧分享

Written by

in

文章目录
  • 在 Python 自动化办公的场景中,openpyxl 库无疑是操作 Excel 文件的首选利器。它不仅能读写数据,还能控制样式、图表和公式。然而,很多初学者在处理大规模数据或涉及金额计算的报表时,往往会遇到两个棘手的问题: 性能瓶颈:当需要遍历几万行数据进行格式化或计算时,简单的 for 循环写法可能导致程序运行极其缓慢,甚至内存溢出。 精度丢失:在处理财务数据时,浮点数运算的“舍入误差”是绝对不能容忍的(例如 0.1 + 0.2 != 0.3)。直接将浮点数写入 Excel 往往会引发业务逻辑错误。 本篇文章将深入探讨如何结合 Python 的 decimal 模块与 openpyxl 的高效循环技巧,打造一个既精准又高效的数据处理脚本。
  • 在处理金额、税率或任何对精度要求极高的数据时,使用 Python 原生的 float 类型是一场灾难。
  • 当你需要处理包含成千上万行数据的 Excel 文件时,低效的循环写法会让你的 CPU 占用率飙升。
  • 让我们把上述知识点结合起来,编写一个完整的脚本。场景:处理一份包含大量交易记录的 CSV(模拟),计算税费,并写入 Excel,要求金额精确,且处理速度快。 import csv from decimal import Decimal, ROUND_HALF_UP from openpyxl import Workbook from openpyxl.styles import Font, Alignment # 模拟生成一个大 CSV 文件(实际中可能是读取外部文件) def generate_mock_csv(filename, rows=50000): with open(filename, ‘w’, newline=”) as f: writer = csv.writer(f) writer.writerow([‘ID’, ‘Amount’, ‘TaxRate’]) for i in range(1, rows + 1): writer.writerow([i, f”{(i % 100) + 100}.50″, “0.08”]) def process_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 并计算 (流式处理) with open(input_csv, ‘r’) as f: reader = csv.reader(f) next(reader) # 跳过表头 batch_data = [] # 缓冲区,批量写入可略微提升性能,但 write_only 模式下 append 已经很快 for row in reader: if not 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) 代码解析: Decimal 的使用:在读取字符串转为 Decimal 时,使用 Decimal(row[1]) 而非 float(row[1]),彻底杜绝精度误差。 quantize 方法:这是控制小数位数和舍入方式的关键。例如 .quantize(Decimal('0.00')) 强制保留两位小数。 write_only 模式:在创建 Workbook 时开启,配合 ws.append(),即使处理 50,000 行数据也能秒级完成,且内存占用极低。 流式读取 CSV:使用 csv 模块逐行读取,不一次性加载大文件到内存。
  • 在 Python 中使用 openpyxl 结合 Decimal 进行数据处理,是企业级开发的标准实践。总结一下核心要点: 数据精度优先:凡是涉及金额、统计、科学计算,务必使用 Decimal 类型,仅在最终展示或写入 Excel 的瞬间转换为 float。 选择正确的读写模式: 小文件(<10MB):常规模式,随意操作。 大文件(>10MB 或 >10万行):必须使用 read_only=True 读取,write_only=True 写入。 避免混合运算:不要让 Decimal 和 float 在同一个公式里混用,这会触发隐式转换,导致精度丢失。 通过以上技巧,你可以轻松应对绝大多数 Excel 数据处理任务,写出既健壮又高效的代码。 到此这篇关于Python使用openpyxl操作Excel的高阶技巧分享的文章就介绍到这了,更多相关Python openpyxl操作Excel内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Python使用openpyxl高效读写与封装Excel的完整指南 Python利用openpyxl与pandas处理Excel多工作表的实战对比 Python使用openpyxl与pandas读取Excel文件的对比详解 Python基于PyQt5和openpyxl实现Excel单元格合并工具 Python操作Excel的实用工具与库openpyxl/pandas的详细指南 Python使用openpyxl读取Excel的操作详解 Python中处理Excel数据的方法对比(pandas和openpyxl) python使用Openpyxl操作Excel文件的实现
  • 目录
    • 一、 为什么你的 Excel 数据处理总是“差一点”?
    • 二、 精度之痛:用 Decimal 拯救你的财务数据
      • 1. 浮点数的“陷阱”
      • 2. Decimal 模块的引入
    • 三、 效率革命:Openpyxl 的高效循环策略
      • 1. 最慢的写法:逐行写入并保存
      • 2. 进阶写法:使用append()批量写入
      • 3. 高阶写法:内存优化与公式填充
      • 4. 终极加速:只读模式与公式缓存
    • 四、 综合实战:构建一个高精度报表生成器
      • 五、 总结与避坑指南

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

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

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

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

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

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

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

        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

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

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

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

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

        如果你是按行顺序写入数据,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])
        

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

        • 关闭自动计算:Excel 打开时会自动重算公式,如果数据量大,建议先写入数据,最后再写入公式,或者在 Python 中计算好结果直接写入值。
        • 利用生成器(Generator):不要一次性把所有数据加载到列表中,使用生成器流式处理数据,减少内存占用。
        # 假设 data_generator 是一个生成器,源源不断地产生数据
        def data_generator():
            for i in range(1, 100000):
                yield [Decimal(i) * Decimal('1.05'), Decimal(i) * Decimal('0.95')]
        
        # 流式写入
        for row_idx, row_data in enumerate(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]))
        

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

        # ❌ 慢
        for row in range(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] is not None:
                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 文件(实际中可能是读取外部文件)
        def generate_mock_csv(filename, rows=50000):
            with open(filename, 'w', newline='') as f:
                writer = csv.writer(f)
                writer.writerow(['ID', 'Amount', 'TaxRate'])
                for i in range(1, rows + 1):
                    writer.writerow([i, f"{(i % 100) + 100}.50", "0.08"])
        
        def process_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 并计算 (流式处理)
            with open(input_csv, 'r') as f:
                reader = csv.reader(f)
                next(reader)  # 跳过表头
                
                batch_data = [] # 缓冲区,批量写入可略微提升性能,但 write_only 模式下 append 已经很快
                
                for row in reader:
                    if not 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)
        

        代码解析:

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

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

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

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

        到此这篇关于Python使用openpyxl操作Excel的高阶技巧分享的文章就介绍到这了,更多相关Python openpyxl操作Excel内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

        您可能感兴趣的文章:

        • Python使用openpyxl高效读写与封装Excel的完整指南
        • Python利用openpyxl与pandas处理Excel多工作表的实战对比
        • Python使用openpyxl与pandas读取Excel文件的对比详解
        • Python基于PyQt5和openpyxl实现Excel单元格合并工具
        • Python操作Excel的实用工具与库openpyxl/pandas的详细指南
        • Python使用openpyxl读取Excel的操作详解
        • Python中处理Excel数据的方法对比(pandas和openpyxl)
        • python使用Openpyxl操作Excel文件的实现

        站内搜索