Python使用openpyxl操作Excel的高階技巧分享
一、 為什么你的 Excel 數(shù)據(jù)處理總是“差一點(diǎn)”?
在 Python 自動(dòng)化辦公的場(chǎng)景中,openpyxl 庫無疑是操作 Excel 文件的首選利器。它不僅能讀寫數(shù)據(jù),還能控制樣式、圖表和公式。然而,很多初學(xué)者在處理大規(guī)模數(shù)據(jù)或涉及金額計(jì)算的報(bào)表時(shí),往往會(huì)遇到兩個(gè)棘手的問題:
- 性能瓶頸:當(dāng)需要遍歷幾萬行數(shù)據(jù)進(jìn)行格式化或計(jì)算時(shí),簡(jiǎn)單的
for循環(huán)寫法可能導(dǎo)致程序運(yùn)行極其緩慢,甚至內(nèi)存溢出。 - 精度丟失:在處理財(cái)務(wù)數(shù)據(jù)時(shí),浮點(diǎn)數(shù)運(yùn)算的“舍入誤差”是絕對(duì)不能容忍的(例如
0.1 + 0.2 != 0.3)。直接將浮點(diǎn)數(shù)寫入 Excel 往往會(huì)引發(fā)業(yè)務(wù)邏輯錯(cuò)誤。
本篇文章將深入探討如何結(jié)合 Python 的 decimal 模塊與 openpyxl 的高效循環(huán)技巧,打造一個(gè)既精準(zhǔn)又高效的數(shù)據(jù)處理腳本。
二、 精度之痛:用 Decimal 拯救你的財(cái)務(wù)數(shù)據(jù)
在處理金額、稅率或任何對(duì)精度要求極高的數(shù)據(jù)時(shí),使用 Python 原生的 float 類型是一場(chǎng)災(zāi)難。
1. 浮點(diǎn)數(shù)的“陷阱”
Python 的 float 遵循 IEEE 754 標(biāo)準(zhǔn),這導(dǎo)致了二進(jìn)制無法精確表示某些十進(jìn)制小數(shù)。
# 經(jīng)典的浮點(diǎn)數(shù)問題 print(0.1 + 0.2) # 輸出: 0.30000000000000004
當(dāng)你把這個(gè)結(jié)果寫入 Excel 時(shí),雖然 Excel 自身也有精度限制,但在數(shù)據(jù)傳輸階段就已經(jīng)埋下了隱患。
2. Decimal 模塊的引入
Python 的 decimal 模塊提供了一種十進(jìn)制浮點(diǎn)運(yùn)算,它能夠完全模擬人工計(jì)算的邏輯。
實(shí)戰(zhàn)技巧:在寫入 Excel 前進(jìn)行轉(zhuǎn)換
在使用 openpyxl 寫入單元格時(shí),我們需要確保數(shù)據(jù)類型是精確的。
from decimal import Decimal, getcontext
# 設(shè)置精度(可選,視業(yè)務(wù)需求而定)
getcontext().prec = 4
# 模擬業(yè)務(wù)數(shù)據(jù)
value_a = Decimal('0.1')
value_b = Decimal('0.2')
result = value_a + value_b # 結(jié)果精確為 Decimal('0.3')
# 在寫入 openpyxl 時(shí),可以直接寫入 Decimal 對(duì)象
# openpyxl 會(huì)自動(dòng)將其轉(zhuǎn)換為浮點(diǎn)數(shù),但為了保險(xiǎn),建議轉(zhuǎn)為 float
ws.cell(row=1, column=1, value=float(result))
核心建議:
- 計(jì)算階段:全程使用
Decimal對(duì)象進(jìn)行加減乘除。 - 寫入階段:將
Decimal結(jié)果轉(zhuǎn)換為float再賦值給ws.cell(),或者直接賦值(openpyxl 會(huì)處理),但務(wù)必在計(jì)算過程中避免混合使用float和Decimal。
三、 效率革命:Openpyxl 的高效循環(huán)策略
當(dāng)你需要處理包含成千上萬行數(shù)據(jù)的 Excel 文件時(shí),低效的循環(huán)寫法會(huì)讓你的 CPU 占用率飆升。
1. 最慢的寫法:逐行寫入并保存
這是一個(gè)典型的錯(cuò)誤示范:
# ? 性能殺手:在循環(huán)中反復(fù)保存或頻繁操作單元格對(duì)象
for row in range(1, 10000):
for col in range(1, 10):
# 每次調(diào)用 ws.cell 都有一定開銷
ws.cell(row=row, column=col, value=row * col)
wb.save('slow_file.xlsx')
這種做法不僅慢,而且如果文件很大,很容易導(dǎo)致內(nèi)存問題。
2. 進(jìn)階寫法:使用append()批量寫入
如果你是按行順序?qū)懭霐?shù)據(jù),append() 方法比逐個(gè) cell() 賦值要快得多。
# ? 推薦:按行追加數(shù)據(jù)
import time
from decimal import Decimal
data_source = [
[Decimal('100.50'), Decimal('200.30')],
[Decimal('101.00'), Decimal('202.00')],
# ... 假設(shè)這里有成千上萬行
]
for row_data in data_source:
# 將 Decimal 轉(zhuǎn)換為 float 或直接寫入
ws.append([float(x) for x in row_data])
3. 高階寫法:內(nèi)存優(yōu)化與公式填充
在處理超大數(shù)據(jù)量時(shí),如果必須逐個(gè)單元格賦值(例如需要根據(jù)上一行計(jì)算下一行),可以使用以下技巧:
- 關(guān)閉自動(dòng)計(jì)算:Excel 打開時(shí)會(huì)自動(dòng)重算公式,如果數(shù)據(jù)量大,建議先寫入數(shù)據(jù),最后再寫入公式,或者在 Python 中計(jì)算好結(jié)果直接寫入值。
- 利用生成器(Generator):不要一次性把所有數(shù)據(jù)加載到列表中,使用生成器流式處理數(shù)據(jù),減少內(nèi)存占用。
# 假設(shè) data_generator 是一個(gè)生成器,源源不斷地產(chǎn)生數(shù)據(jù)
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):
# 這里的邏輯比較復(fù)雜,因?yàn)?openpyxl 的 append 是最快的
# 如果必須使用 cell 賦值,請(qǐng)注意減少屬性訪問次數(shù)
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 列,計(jì)算后寫入 B 列,不要在循環(huán)中反復(fù)讀取單元格。
# ? 慢
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)
# ? 快 (先批量讀取到內(nèi)存,再批量計(jì)算,最后寫入)
# 但對(duì)于超大文件,這會(huì)撐爆內(nèi)存,所以折中方案是:
# 1. 將 ws.max_row 分段處理
# 2. 或者使用 openpyxl 的 read_only 模式讀取,計(jì)算,然后用 write_only 模式寫入新文件。
最佳實(shí)踐:read_only 與 write_only 模式
這是處理超大 Excel 文件(如 50MB+)的必殺技。
from openpyxl import load_workbook, Workbook
# 1. 以只讀模式加載源文件(極低內(nèi)存占用)
wb_read = load_workbook('big_data.xlsx', read_only=True)
ws_read = wb_read.active
# 2. 創(chuàng)建新工作簿(或以 write_only 模式保存)
wb_write = Workbook(write_only=True)
ws_write = wb_write.create_sheet()
# 3. 循環(huán)處理
# read_only 模式下,只能使用 ws.iter_rows() 遍歷
for row in ws_read.iter_rows(values_only=True):
# row 是一個(gè)元組,包含該行的所有值
# 這里進(jìn)行 Decimal 計(jì)算
if row[0] is not None:
val_a = Decimal(str(row[0])) # 轉(zhuǎn)換為 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')
這種模式下,內(nèi)存占用極低,因?yàn)閿?shù)據(jù)是流式讀取和寫入的,不會(huì)一次性加載到內(nèi)存中。
四、 綜合實(shí)戰(zhàn):構(gòu)建一個(gè)高精度報(bào)表生成器
讓我們把上述知識(shí)點(diǎn)結(jié)合起來,編寫一個(gè)完整的腳本。場(chǎng)景:處理一份包含大量交易記錄的 CSV(模擬),計(jì)算稅費(fèi),并寫入 Excel,要求金額精確,且處理速度快。
import csv
from decimal import Decimal, ROUND_HALF_UP
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment
# 模擬生成一個(gè)大 CSV 文件(實(shí)際中可能是讀取外部文件)
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. 設(shè)置 Decimal 上下文
# ROUND_HALF_UP: 四舍五入,0.5 向上進(jìn)位
Decimal('0.01').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
# 4. 讀取 CSV 并計(jì)算 (流式處理)
with open(input_csv, 'r') as f:
reader = csv.reader(f)
next(reader) # 跳過表頭
batch_data = [] # 緩沖區(qū),批量寫入可略微提升性能,但 write_only 模式下 append 已經(jīng)很快
for row in reader:
if not row: continue
raw_id = int(row[0])
raw_amount = Decimal(row[1])
tax_rate = Decimal(row[2])
# 計(jì)算邏輯 (Decimal 精度)
tax_amount = (raw_amount * tax_rate).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
total_amount = raw_amount + tax_amount
# 準(zhǔn)備寫入數(shù)據(jù) (轉(zhuǎn)換為 float 或保留 Decimal)
# openpyxl 支持寫入 Decimal,但為了顯式控制,我們轉(zhuǎn)為 float
# 注意:如果 Excel 僅用于展示,float 足夠;若需二次計(jì)算,建議轉(zhuǎn)為字符串或保留 Decimal
# 這里我們轉(zhuǎn)為 float 展示
row_data = [
raw_id,
float(raw_amount),
float(tax_rate),
float(tax_amount),
float(total_amount)
]
ws.append(row_data)
# 簡(jiǎn)單的進(jìn)度提示(實(shí)際生產(chǎn)中可使用 tqdm)
if raw_id % 10000 == 0:
print(f"已處理 {raw_id} 行數(shù)據(jù)...")
# 5. 保存文件
print(f"正在保存文件: {output_xlsx}")
wb.save(output_xlsx)
print("完成!")
# 執(zhí)行演示
if __name__ == "__main__":
input_csv = 'mock_transactions.csv'
output_xlsx = 'financial_report.xlsx'
# 生成測(cè)試數(shù)據(jù)
print("正在生成模擬數(shù)據(jù)...")
generate_mock_csv(input_csv, rows=50000)
# 處理數(shù)據(jù)
process_financial_report(input_csv, output_xlsx)
代碼解析:
Decimal的使用:在讀取字符串轉(zhuǎn)為Decimal時(shí),使用Decimal(row[1])而非float(row[1]),徹底杜絕精度誤差。quantize方法:這是控制小數(shù)位數(shù)和舍入方式的關(guān)鍵。例如.quantize(Decimal('0.00'))強(qiáng)制保留兩位小數(shù)。write_only模式:在創(chuàng)建Workbook時(shí)開啟,配合ws.append(),即使處理 50,000 行數(shù)據(jù)也能秒級(jí)完成,且內(nèi)存占用極低。- 流式讀取 CSV:使用
csv模塊逐行讀取,不一次性加載大文件到內(nèi)存。
五、 總結(jié)與避坑指南
在 Python 中使用 openpyxl 結(jié)合 Decimal 進(jìn)行數(shù)據(jù)處理,是企業(yè)級(jí)開發(fā)的標(biāo)準(zhǔn)實(shí)踐。總結(jié)一下核心要點(diǎn):
- 數(shù)據(jù)精度優(yōu)先:凡是涉及金額、統(tǒng)計(jì)、科學(xué)計(jì)算,務(wù)必使用
Decimal類型,僅在最終展示或?qū)懭?Excel 的瞬間轉(zhuǎn)換為float。 - 選擇正確的讀寫模式:
- 小文件(<10MB):常規(guī)模式,隨意操作。
- 大文件(>10MB 或 >10萬行):必須使用
read_only=True讀取,write_only=True寫入。
- 避免混合運(yùn)算:不要讓
Decimal和float在同一個(gè)公式里混用,這會(huì)觸發(fā)隱式轉(zhuǎn)換,導(dǎo)致精度丟失。
通過以上技巧,你可以輕松應(yīng)對(duì)絕大多數(shù) Excel 數(shù)據(jù)處理任務(wù),寫出既健壯又高效的代碼。
到此這篇關(guān)于Python使用openpyxl操作Excel的高階技巧分享的文章就介紹到這了,更多相關(guān)Python openpyxl操作Excel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python使用openpyxl高效讀寫與封裝Excel的完整指南
- Python利用openpyxl與pandas處理Excel多工作表的實(shí)戰(zhàn)對(duì)比
- Python使用openpyxl與pandas讀取Excel文件的對(duì)比詳解
- Python基于PyQt5和openpyxl實(shí)現(xiàn)Excel單元格合并工具
- Python操作Excel的實(shí)用工具與庫openpyxl/pandas的詳細(xì)指南
- Python使用openpyxl讀取Excel的操作詳解
- Python中處理Excel數(shù)據(jù)的方法對(duì)比(pandas和openpyxl)
- python使用Openpyxl操作Excel文件的實(shí)現(xiàn)
相關(guān)文章
結(jié)合Python網(wǎng)絡(luò)爬蟲做一個(gè)今日新聞小程序
本篇文章介紹了我在開發(fā)過程中遇到的一個(gè)問題,以及解決該問題的過程及思路,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下2021-09-09
PyQt5連接MySQL及QMYSQL driver not loaded錯(cuò)誤解決
這篇文章主要介紹了PyQt5連接MySQL及QMYSQL driver not loaded錯(cuò)誤解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Pytorch Dataset,TensorDataset,Dataloader,Sampler關(guān)系解讀
這篇文章主要介紹了Pytorch Dataset,TensorDataset,Dataloader,Sampler關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
Python報(bào)表自動(dòng)化之從數(shù)據(jù)到可視化一站式指南
在現(xiàn)代數(shù)據(jù)驅(qū)動(dòng)的世界中,生成清晰、有用的報(bào)表對(duì)于業(yè)務(wù)決策至關(guān)重要,Python作為一門強(qiáng)大的編程語言,提供了豐富的庫和工具,使得報(bào)表自動(dòng)化變得輕而易舉,本文將詳細(xì)介紹如何利用Python從數(shù)據(jù)處理到可視化,實(shí)現(xiàn)報(bào)表自動(dòng)化的全過程2024-01-01
使用Python編寫一個(gè)自動(dòng)化辦公小助手
在日常辦公中,我們常常會(huì)遇到一些重復(fù)性的任務(wù),如批量處理文件,發(fā)送郵件等,本文我們將使用Python 編寫一個(gè)自動(dòng)化辦公小助手,幫助你高效完成這些任務(wù)2025-10-10
linux系統(tǒng)使用python獲取內(nèi)存使用信息腳本分享
這篇文章主要介紹了linux系統(tǒng)使用python獲取內(nèi)存使用情況信息,大家參考使用吧2014-01-01

