在使用 python-docx 自動化處理 Word 文件時,我們通常操作的是 Paragraph 物件。但當你需要更精細的控制(例如遍歷 document.element.body)時,你會遇到它的真面目——CT_P。
這兩者看似相似,但在屬性存取上有著關鍵的設計哲學差異。
1. 角色介紹
- Paragraph (高層物件)
- 身分:它是
python-docx封裝好的「代理人」。 - 特點:介面友善,屬性通常會自動處理型別轉換(例如把樣式轉為物件,而不只是字串 ID)。
- 取得方式:
doc.paragraphs[0]
- 身分:它是
- CT_P (底層元素)
- 身分:它是 XML 結構在 Python 中的直接映射 (Complex Type Paragraph)。
- 特點:它是資料的「本體」。雖然它是 XML 元素,但
python-docx貼心地為它加上了一些 Python 屬性(如.text),讓它比純 XML 更強大。 - 取得方式:
paragraph._element或直接遍歷doc.element.body
2. 實戰比對:讀取文字 (.text)
這是最容易讓人誤會的地方。兩者都能讀取文字,但背後的機制不同。
程式碼範例
from docx import Document
doc = Document(r"D:\Temp\demo.docx")
p_obj = doc.paragraphs[0] # 取得高層物件
ct_p = p_obj._element # 取得底層物件 (CT_P)
# --- 1. 讀取文字 ---
print(f"Paragraph 文字: {p_obj.text}")
print(f"CT_P 文字: {ct_p.text}")差異解析
- Paragraph.text:
標準用法。它會遍歷段落內所有的Run,將文字接起來回傳。 - CT_P.text:
這是一個特異功能。 在標準lxml(XML 解析庫) 中,父節點通常讀不到子節點的文字。但python-docx的作者在CT_P類別中手動覆寫了.text屬性,讓它去掃描底下所有的<w:t>標籤。- 結論:在讀取純文字時,兩者效果完全一樣。
3. 實戰比對:讀取樣式 (.style)
這裡就是「坑」的所在了。兩者回傳的資料型態完全不同。
程式碼範例
# --- 2. 讀取樣式 ---
# A. 使用 Paragraph (高層)
style_obj = p_obj.style
print(f"Paragraph 樣式型態: {type(style_obj)}")
print(f"Paragraph 樣式名稱: {style_obj.name}") # 例如: 'Heading 1'
# B. 使用 CT_P (底層)
style_id = ct_p.style
print(f"CT_P 樣式型態: {type(style_id)}")
print(f"CT_P 樣式 ID: {style_id}") # 例如: '1' 或 'Heading1'差異解析
- Paragraph.style:
- 回傳的是一個
_ParagraphStyle物件。 - 你可以透過它進一步存取字型、顏色等詳細設定 (
style_obj.font.color...)。 - 它顯示的是人類可讀的名稱 (如 “Heading 1”)。
- 回傳的是一個
- CT_P.style:
- 回傳的是一個 字串 (String) (或者是
None)。 - 這是 Word 內部使用的 Style ID (XML 屬性
w:val)。 - 注意:Word 的內部 ID 常常跟顯示名稱不同!
- 顯示名稱:”Heading 1″
- 內部 ID:”1″ (或是 “Heading1″,取決於 Word 版本和語言)
- 顯示名稱:”內文”
- 內部 ID:可能為
None(代表預設樣式)
- 回傳的是一個 字串 (String) (或者是
4. 總結與建議
什麼時候該用哪一個?
- 一般開發 (90% 的情況):
請乖乖使用Paragraph。它幫你處理了所有髒活,程式碼可讀性最高。 - 進階遍歷 (混合內容):
當你需要依序讀取文件中的「段落」與「表格」時(因為doc.paragraphs會跳過表格),你必須遍歷doc.element.body。
這時你拿到的都是CT_P(段落) 或CT_Tbl(表格)。- 在這種情況下,直接呼叫
body[i].text是最乾淨俐落的寫法,不需要為了讀文字特地把它包裝回 Paragraph 物件。
- 在這種情況下,直接呼叫
推薦hahow線上學習python: https://igrape.net/30afN