Python 檔案操作(File I/O)─ tempfile 模組完整教學
簡介
在日常開發與測試過程中,我們常常需要 暫時寫入資料、產生臨時檔案或目錄,卻不想讓這些檔案永久留在磁碟上。
如果手動建立、刪除檔案,不僅程式碼會變雜,也容易遺漏清理步驟,導致磁碟空間被無意佔用,甚至在多執行緒/多程序環境下產生競爭條件(race condition)。
Python 標準庫提供的 tempfile 模組正是為了解決這類問題而設計的。它能 安全、跨平台 地產生唯一的暫存檔案或目錄,並在不需要時自動刪除,讓開發者可以把注意力集中在核心業務邏輯上。
本篇文章將從 核心概念、實作範例、常見陷阱與最佳實踐,到 真實應用場景,一步步帶你熟悉 tempfile,成為檔案 I/O 的好幫手。
核心概念
1. 為什麼要使用 tempfile?
- 唯一性:
tempfile會在系統的暫存目錄(如 Windows 的%TEMP%、Linux 的/tmp)自動產生唯一檔名,避免檔名衝突。 - 安全性:使用
tempfile產生的檔案會設定適當的檔案權限(預設只能被建立它的使用者讀寫),降低資訊外洩風險。 - 自動清理:配合
with語法或TemporaryFile、NamedTemporaryFile的delete=True(預設)參數,檔案會在關閉時自動移除。 - 跨平台:不必自行判斷暫存目錄位置,
tempfile會根據作業系統自動選擇最合適的路徑。
2. 主要類別與函式
| 類別 / 函式 | 目的 | 常用參數 | 重要說明 |
|---|---|---|---|
tempfile.TemporaryFile() |
產生 匿名 暫存檔(只能透過檔案物件操作) | mode='w+b', encoding=None, newline=None |
檔案關閉即被刪除,適合只在程式內部使用的臨時資料。 |
tempfile.NamedTemporaryFile() |
產生 有名稱 的暫存檔,允許其他程式或子程序存取 | mode='w+b', delete=True, prefix='', suffix='' |
delete=False 時需自行手動刪除。 |
tempfile.TemporaryDirectory() |
產生 暫時目錄,內可自行新增檔案 | suffix='', prefix='tmp' |
目錄關閉(或程式結束)時會遞迴刪除所有內容。 |
tempfile.mkstemp() |
返回 檔案描述符 與 路徑(低階 API) | suffix='', prefix='tmp', dir=None |
必須自行使用 os.close(fd) 並刪除檔案。 |
tempfile.mkdtemp() |
返回 目錄路徑(低階 API) | 同上 | 必須自行刪除目錄。 |
小技巧:若需要 二進位 或 文字 模式,只要在
mode參數中指定'b'或't'(如'w+b'、'r+t')即可,tempfile會自動處理編碼。
3. with 語法的好處
使用 with(上下文管理器)可以確保 檔案/目錄在離開區塊時自動關閉與刪除,即使發生例外也不會遺漏清理。例如:
with tempfile.NamedTemporaryFile(mode='w+', delete=True) as tmp:
tmp.write('Hello, world!')
tmp.seek(0)
print(tmp.read())
# 離開 with 區塊後檔案已自動刪除
程式碼範例
以下示範 5 個實用案例,涵蓋最常見的需求。每段程式碼都加上說明,方便初學者快速上手。
範例 1:建立匿名暫存檔,寫入二進位資料
import tempfile
# 使用 TemporaryFile 產生匿名檔,預設為二進位寫入模式 'w+b'
with tempfile.TemporaryFile() as tmp:
data = b'\x00\xFF\x7A\x3C'
tmp.write(data) # 寫入二進位資料
tmp.seek(0) # 重新定位到檔案開頭
read_back = tmp.read()
print('讀回資料:', read_back)
# 離開 with 區塊後檔案已自動刪除,無檔名可見
重點:匿名檔案在檔案系統中沒有實體檔名,適合只在程式內部暫存資料的情況。
範例 2:產生可被外部程式讀取的命名暫存檔
import tempfile
import subprocess
# 建立一個可被外部指令讀取的暫存檔
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as tmp:
tmp.write('這是一段測試文字\n第二行內容')
tmp_path = tmp.name # 取得實際路徑
print('暫存檔路徑:', tmp_path)
# 使用系統指令 cat (Linux/macOS) 或 type (Windows) 讀取檔案
subprocess.run(['cat', tmp_path]) # Linux/macOS 範例
# 手動刪除檔案(因為 delete=False)
import os
os.remove(tmp_path)
注意:
delete=False必須自行呼叫os.remove(),否則檔案會留在磁碟上。
範例 3:建立臨時目錄,並在其中產生多個檔案
import tempfile
import os
with tempfile.TemporaryDirectory() as tmp_dir:
print('暫時目錄路徑:', tmp_dir)
# 在臨時目錄裡寫入三個測試檔案
for i in range(3):
file_path = os.path.join(tmp_dir, f'file_{i}.txt')
with open(file_path, 'w') as f:
f.write(f'第 {i+1} 個檔案的內容')
print('已建立:', file_path)
# 此時可以把目錄路徑傳給其他函式或子程序使用
# ...
# 離開 with 區塊後,tmp_dir 以及裡面的檔案全部被刪除
實務:臨時目錄常用於 解壓縮、影像處理、單元測試 等需要臨時儲存多個檔案的情境。
範例 4:使用低階 API mkstemp 手動管理檔案描述符
import os, tempfile
fd, path = tempfile.mkstemp(suffix='.log')
try:
# 直接使用檔案描述符寫入文字(轉換為二進位)
os.write(fd, b'Log start\n')
os.write(fd, b'Info: 程式執行中...\n')
finally:
os.close(fd) # 必須關閉描述符
print('暫存檔路徑:', path)
# 手動刪除檔案
os.remove(path)
適用情況:需要 檔案描述符(例如在
os.exec*、subprocess.Popen中傳遞)時,mkstemp是首選。
範例 5:在測試環境中使用 TemporaryDirectory 作為 pytest fixture
# conftest.py
import pytest, tempfile, shutil
@pytest.fixture
def temp_dir():
# 建立臨時目錄,測試結束後自動清除
dir_path = tempfile.mkdtemp()
yield dir_path
shutil.rmtree(dir_path) # 手動刪除目錄
# test_example.py
def test_write_file(temp_dir):
file_path = f'{temp_dir}/data.txt'
with open(file_path, 'w') as f:
f.write('測試內容')
with open(file_path, 'r') as f:
assert f.read() == '測試內容'
好處:將臨時目錄封裝成 fixture,每個測試都能得到乾淨的工作環境,避免測試之間互相干擾。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 忘記刪除檔案 | 使用 delete=False 或低階 API (mkstemp、mkdtemp) 時,若忘記手動清理會造成磁碟累積。 |
養成使用 with 或在 finally 區塊中呼叫 os.remove()、shutil.rmtree()。 |
| 檔案權限不當 | 在共用伺服器上產生的暫存檔可能被其他使用者讀取。 | tempfile 預設設定最小權限,若自行建立檔案,請使用 os.open(..., 0o600)。 |
| 同時寫入同一暫存檔 | 多執行緒/多程序同時寫入同一 NamedTemporaryFile 可能導致 race condition。 |
為每個執行緒/程序產生 獨立 暫存檔;或使用 multiprocessing 的 tempfile 共享機制。 |
在 Windows 上無法重新開啟已開啟的 NamedTemporaryFile |
Windows 預設會以 排他模式 開啟檔案,導致外部程式無法讀取。 | 使用 delete=False 並在建立後立即關閉 (tmp.close()),或在 NamedTemporaryFile 中設定 delete=False 並自行管理刪除。 |
| 忘記設定檔案模式 | 預設模式是二進位 ('w+b'),若寫入文字卻未指定編碼會拋出 TypeError。 |
明確指定 mode='w+'(文字)或 mode='w+b'(二進位),必要時加上 encoding='utf-8'。 |
最佳實踐清單
- 盡量使用
with:確保檔案/目錄在離開作用域時自動關閉與刪除。 - 只在需要時設定
delete=False,並在程式結尾或finally區塊手動清理。 - 在測試環境 使用
TemporaryDirectory作為 fixture,保證每次測試都有乾淨的工作目錄。 - 避免硬編碼暫存目錄,改用
tempfile.gettempdir()取得系統預設路徑。 - 考慮檔案大小:若暫存資料非常大,建議使用
TemporaryFile(不會寫入磁碟,直接在記憶體或磁碟交換空間),或使用shutil.move()於處理完畢後搬移到永久位置。
實際應用場景
| 場景 | 為什麼適合使用 tempfile |
|---|---|
| 單元測試 | 測試函式需要讀寫檔案時,TemporaryDirectory 能提供獨立、乾淨的環境,測試完成後自動清理。 |
| 資料處理流水線 | 在 ETL 流程中,先把外部來源資料寫入暫存檔,完成清洗後再搬移至正式資料庫或雲端儲存。 |
| Web 應用上傳 | 使用 NamedTemporaryFile 暫時保存使用者上傳的檔案,驗證完畢後再永久儲存或刪除。 |
| 子程序或外部指令 | 需要將資料傳給 ffmpeg、ImageMagick 等命令列工具時,先把資料寫入 NamedTemporaryFile,讓指令直接讀取路徑。 |
| 多執行緒/多程序協同 | 每個執行緒產生自己的暫存檔或目錄,避免競爭條件,最終在主程式彙總結果。 |
案例說明:假設你在開發一個圖片壓縮服務,使用者上傳圖片後,程式會先把原圖寫入
NamedTemporaryFile,呼叫subprocess.run(['ffmpeg', '-i', tmp_path, ...])進行壓縮,壓縮完成後再把結果搬到永久儲存區,最後刪除暫存檔。整個流程全程使用tempfile,既安全又不會留下垃圾檔案。
總結
tempfile 是 Python 檔案 I/O 中不可或缺的工具,提供:
- 唯一且安全的暫存檔/目錄 產生方式
- 自動清理 機制,降低資源泄漏風險
- 跨平台 支援,省去手動判斷暫存目錄的麻煩
透過本文的 核心概念、實作範例、陷阱與最佳實踐,你現在應該可以:
- 快速建立 匿名或具名稱的暫存檔、目錄
- 安全地在多執行緒/多程序環境 中使用暫存檔
- 在單元測試、Web 上傳、資料處理 等真實情境中,運用
tempfile提升程式的可維護性與可靠性
記得 養成使用 with 的好習慣,並在需要手動管理時,務必在 finally 區塊中清理資源。只要掌握這些要點,你的 Python 檔案操作將變得更 乾淨、可靠且易於維護。
祝你在程式開發的道路上,暫存檔永遠不會成為「垃圾」! 🚀