Python 的字串處理看似簡單,卻隱藏著許多陷阱,尤其在編碼和解碼方面。許多開發者經常在 bytesstrunicode 之間迷失,導致程式出現難以預料的錯誤。本文將解析 Python 2 和 Python 3 在字串處理上的差異,並提供實用的技巧和避雷,讓您輕鬆駕馭 Python 字串,避免編碼問題。在 Python 2 的世界中,strunicode 分別代表原始 8 位元值和 Unicode 字元,而 Python 3 則改用 bytesstr 來表示這兩種資料型別。這種差異是許多 Python 2 程式碼遷移到 Python 3 時出現問題的根源。為瞭解決這個問題,我們需要在程式碼中明確處理編碼和解碼的過程。UTF-8 作為最常用的編碼方式,在 Python 字串處理中扮演著重要的角色。玄貓建議,在程式介面的最外層進行 Unicode 的編碼和解碼,而程式的核心邏輯應該使用 Unicode 字元型別,例如 Python 3 的 str 和 Python 2 的 unicode,並且不對字元的編碼做任何假設。

提升Python技能:專家建議與實戰技巧

本章旨在提供Python開發者一套實用的,透過具體的例項和專家建議,協助讀者提升Python程式設計的技能。玄貓將帶領大家深入瞭解Python的各種特性,並提供改善程式碼品質和效率的方法。

閱讀EPUB的注意事項

EPUB是一種開放的電子書格式,但不同閱讀裝置和應用程式對EPUB的支援程度有所不同。為了獲得最佳閱讀體驗,建議調整裝置或應用程式的設定,例如字型、字型大小、單欄或雙欄顯示,以及橫向或縱向模式。若想了解更多關於裝置或應用程式的設定和功能,請參考裝置製造商的網站。

許多書籍包含程式碼或組態範例。為了最佳化這些元素的呈現,建議以單欄橫向模式閱讀,並將字型大小調整到最小。除了以可重排文字格式呈現程式碼和組態外,本章還包含程式碼圖片,模擬印刷書籍中的呈現方式。如果可重排格式可能影響程式碼的呈現,您會看到一個連結,點選後可檢視印刷品質的程式碼圖片。點選裝置或應用程式上的「傳回」按鈕可回到前一頁。

本章讚譽

本章獲得了多位業界專家的讚譽,他們認為本章內容涵蓋廣泛,從基礎的PEP8規範到進階的API設計、測試和效能測量,為Python程式設計師提供了全面的指導。無論是新手還是有經驗的開發者,都能從本章中受益。

Python進階之路:從新手到專家的實用

本章匯集了Python開發中的各種「必知」知識,涵蓋了從PEP8規範、Python慣用語,到函式、方法和類別設計,以及高效使用標準函式庫、API設計、測試和效能測量等各個方面。透過閱讀本章,你可以避免在開發過程中反覆重寫程式碼,並能更深入地理解Python的精髓。

提升程式碼品質的關鍵技巧

本章不僅能幫助你提升程式碼的執行速度,還能讓你寫出更易讀、更Pythonic的程式碼。例如,你可以學習如何使用內建資料結構、強制使用僅限關鍵字的引數,以及使用zip平行迭代列表。這些技巧能讓你的程式碼更簡潔、更高效。

適用於各種程度的Python開發者

無論你是剛從其他語言轉向Python的新手,還是已經有多年經驗的Python開發者,本章都能為你提供寶貴的知識和技巧。對於新手來說,本章能幫助你快速掌握Python的核心概念和最佳實踐;對於有經驗的開發者來說,本章能讓你更深入地瞭解Python的各種特性,並能幫助你發現一些你可能從未遇到過的語言特性。

感謝

感謝我的家人,無論是現在還是過去,他們的支援和愛是我前進的動力。

本章為Python開發者提供了一份寶貴的資源,透過學習書中的技巧和建議,你可以寫出更高效、更易讀、更Pythonic的程式碼,並能提升你的Python程式設計技能。

前言

Python 程式語言擁有獨特的優勢和魅力,但這些特質有時難以掌握。許多熟悉其他語言的程式設計師,常常以受限的思維模式來使用 Python,未能充分發揮其表達能力。另一方面,有些程式設計師則矯枉過正,過度使用 Python 的特性,反而可能在日後引發重大問題。

本章旨在探討以 Pythonic 的方式編寫程式:也就是運用 Python 的最佳實踐。本章建立在你已具備的 Python 基礎知識之上。新手程式設計師將學習 Python 各項功能的最佳實踐,而經驗豐富的程式設計師則能學會如何自信地擁抱這項新工具的獨特性。

玄貓的目標是讓你充分準備好,利用 Python 創造巨大影響。

本章內容概要

本章的每個章節都包含一組廣泛但相關的主題。你可以自由地在各個主題之間跳躍,跟隨你的興趣。每個主題都包含簡潔而明確的指導,說明如何更有效地編寫 Python 程式。主題涵蓋了該做什麼、該避免什麼、如何取得適當的平衡,以及為什麼這是最佳選擇等建議。

本章的主題適用於 Python 3 和 Python 2 的程式設計師(參見第 1 項:「瞭解你使用的 Python 版本」)。使用 Jython、IronPython 或 PyPy 等替代執行環境的程式設計師,也會發現大多數主題都適用。

第 1 章:Pythonic 思維

Python 社群已開始使用 Pythonic 這個形容詞來描述遵循特定風格的程式碼。Python 的慣用語是隨著時間的推移,透過使用該語言並且他人合作的經驗而產生的。本章涵蓋了在 Python 中完成最常見事情的最佳方法。

第 2 章:函式

Python 中的函式具有各種額外的功能,可以讓程式設計師的生活更輕鬆。有些功能與其他程式語言的功能相似,但許多功能是 Python 獨有的。本章涵蓋如何使用函式來闡明意圖、促進重用並減少錯誤。

第 3 章:類別與繼承

Python 是一種物件導向語言。在 Python 中完成事情通常需要編寫新的類別,並定義它們如何透過其介面和層次結構進行互動。本章涵蓋如何使用類別和繼承來表達你希望物件實作的行為。

使用哪個 Python 版本?玄貓的建議

在開始 Python 程式設計之前,選擇合適的 Python 版本至關重要。目前主要有兩個主要版本:Python 2 和 Python 3。雖然 Python 3 是現在的主流,但仍有許多舊專案使用 Python 2。

  • Python 3:導向未來

    Python 3 解決了 Python 2 中的一些設計缺陷,並引入了許多新功能。它是 Python ,也是新專案的首選。

  • Python 2:傳統的選擇

    Python 2 雖然已停止維護,但由於其龐大的程式函式庫和框架生態系統,仍然在某些舊專案中使用。然而,對於新專案,玄貓強烈建議使用 Python 3。

  • 如何檢查 Python 版本

    在命令列中輸入 python --versionpython3 --version 可以檢視你係統上安裝的 Python 版本。

  • 版本選擇的考量

    • 新專案: 始終選擇 Python 3。
    • 現有專案: 評估遷移到 Python 3 的成本和收益。如果遷移成本過高,可以繼續使用 Python 2,但請注意其安全風險。
  • 玄貓的經驗

    玄貓在過去幾年中,已經將所有專案遷移到 Python 3。雖然遷移過程可能需要一些時間和精力,但從長遠來看,這是值得的。Python 3 提供了更好的效能、更多的功能和更強大的安全性。

遵循 PEP 8 風格:提升程式碼可讀性

PEP 8 是 Python 的官方風格,它提供了一套關於如何編寫 Python 程式碼的建議。遵循 PEP 8 可以使你的程式碼更具可讀性、更易於維護,並且 Python 社群的風格保持一致。

  • PEP 8 的主要原則

    • 縮排: 使用 4 個空格進行縮排。
    • 行長度: 每行不超過 79 個字元。
    • 空行: 使用空行分隔函式、類別和邏輯塊。
    • 命名: 使用有意義的名稱,並遵循 PEP 8 的命名約定。
    • 註解: 編寫清晰簡潔的註解,解釋程式碼的功能。
  • 工具輔助

    可以使用 flake8autopep8 等工具來自動檢查和修正程式碼中的 PEP 8 違規。

  • 玄貓的經驗

    玄貓發現,遵循 PEP 8 可以顯著提高程式碼的可讀性和可維護性。雖然一開始可能需要一些時間來適應,但從長遠來看,這是值得的。

字串、位元組與 Unicode 的差異:徹底搞懂編碼

在 Python 中,字串(str)、位元組(bytes)和 Unicode 是不同的資料型別,它們用於表示文字資料。理解它們之間的差異對於正確處理文字資料至關重要。

  • 字串(str):

    用於表示 Unicode 文字。在 Python 3 中,所有字串都是 Unicode 字串。

  • 位元組(bytes):

    用於表示原始位元組資料。位元組物件是不可變的序列,其中每個元素都是一個 0 到 255 之間的整數。

  • Unicode:

    一種字元編碼標準,它為世界上幾乎所有字元都分配了一個唯一的數字程式碼。

  • 編碼與解碼

    • 編碼: 將 Unicode 字串轉換為位元組序列的過程。
    • 解碼: 將位元組序列轉換為 Unicode 字串的過程。
  • 常見的編碼方式

    • UTF-8:一種通用的 Unicode 編碼方式,它可以表示世界上幾乎所有字元。
    • ASCII:一種較舊的編碼方式,只能表示 128 個字元。
  • 玄貓的建議

    在處理文字資料時,始終使用 Unicode 字串。在需要將文字資料儲存到檔案或透過網路傳輸時,才將其編碼為位元組序列。

使用輔助函式取代複雜表示式:程式碼清晰化的關鍵

當程式碼中的表示式變得過於複雜時,可以使用輔助函式來提高程式碼的可讀性和可維護性。輔助函式將複雜的邏輯封裝在一個獨立的函式中,使程式碼更易於理解和測試。

  • 輔助函式的好處

    • 提高可讀性: 將複雜的邏輯分解為更小的、更易於理解的函式。
    • 提高可維護性: 易於測試和修改。
    • 提高重用性: 可以在多個地方重用。
  • 何時使用輔助函式

    • 當表示式過於複雜時。
    • 當需要在多個地方使用相同的邏輯時。
    • 當需要測試複雜邏輯時。
  • 玄貓的經驗

    玄貓發現,使用輔助函式可以顯著提高程式碼的可讀性和可維護性。即使一開始需要一些額外的工作,但從長遠來看,這是值得的。

序列切片:掌握資料處理的利器

序列切片是一種從序列(例如列表、元組和字串)中提取子序列的強大技術。Python 提供了靈活的切片語法,可以輕鬆地提取序列的任何部分。

  • 切片語法

    sequence[start:end:stride]

    • start:切片的起始索引(包含)。
    • end:切片的結束索引(不包含)。
    • stride:切片的步長。
  • 預設值

    • start 的預設值為 0。
    • end 的預設值為序列的長度。
    • stride 的預設值為 1。
  • 切片的應用

    • 提取序列的子序列。
    • 反轉序列。
    • 複製序列。
  • 玄貓的建議

    熟悉切片語法,並善用它來處理序列資料。

避免在單一切片中使用 start、end 和 stride:程式碼簡潔至上

雖然 Python 的切片語法非常靈活,但在單一切片中使用 startendstride 三個引數可能會導致程式碼難以理解。為了提高程式碼的可讀性,建議避免在單一切片中使用三個引數。

  • 替代方案

    • 如果只需要提取序列的子序列,可以使用 startend 引數。
    • 如果需要反轉序列,可以使用 stride 引數。
    • 如果需要同時提取子序列和反轉序列,可以先提取子序列,然後再反轉它。
  • 玄貓的經驗

    玄貓發現,避免在單一切片中使用三個引數可以使程式碼更易於理解和維護。

使用列表推導式取代 map 和 filter:Pythonic 的選擇

列表推導式是一種從現有列表中建立新列表的簡潔方法。它比 mapfilter 函式更具可讀性,並且通常更有效率。

  • 列表推導式的語法

    [expression for item in iterable if condition]

    • expression:用於計算新列表元素的表示式。
    • item:可迭代物件中的每個元素。
    • iterable:可迭代物件,例如列表、元組或字串。
    • condition:一個可選的條件,用於過濾可迭代物件中的元素。
  • 列表推導式的優點

    • 更具可讀性。
    • 通常更有效率。
    • 更 Pythonic。
  • 玄貓的建議

    在可能的情況下,使用列表推導式取代 mapfilter 函式。

避免在列表推導式中使用超過兩個表示式:保持程式碼清晰

雖然列表推導式是一種強大的工具,但在一個列表推導式中使用超過兩個表示式可能會導致程式碼難以理解。為了提高程式碼的可讀性,建議避免在列表推導式中使用超過兩個表示式。

  • 替代方案

    • 可以使用多個列表推導式。
    • 可以使用輔助函式。
  • 玄貓的經驗

    玄貓發現,避免在列表推導式中使用超過兩個表示式可以使程式碼更易於理解和維護。

大型推導式考慮使用產生器表示式:節省記憶體

對於大型列表推導式,可以考慮使用產生器表示式來節省記憶體。產生器表示式不會立即建立列表,而是傳回一個產生器物件,該物件可以逐個產生列表元素。

  • 產生器表示式的語法

    (expression for item in iterable if condition)

    • expression:用於計算新列表元素的表示式。
    • item:可迭代物件中的每個元素。
    • iterable:可迭代物件,例如列表、元組或字串。
    • condition:一個可選的條件,用於過濾可迭代物件中的元素。
  • 產生器表示式的優點

    • 節省記憶體。
    • 可以處理無限序列。
  • 玄貓的建議

    對於大型列表推導式,可以考慮使用產生器表示式來節省記憶體。

優先使用 enumerate 取代 range:程式碼更簡潔易讀

在迴圈中需要同時使用索引和元素時,優先使用 enumerate 函式取代 range 函式。enumerate 函式傳回一個迭代器,該迭代器產生包含索引和元素的元組。

  • enumerate 的用法
my_list = ['a', 'b', 'c']
for index, element in enumerate(my_list):
    print(f'Index: {index}, Element: {element}')
  • enumerate 的優點

    • 更簡潔。
    • 更易讀。
    • 更 Pythonic。
  • 玄貓的建議

    在迴圈中需要同時使用索引和元素時,優先使用 enumerate 函式取代 range 函式。

使用 zip 平行處理迭代器:資料同步的技巧

zip 函式可以用於平行處理多個迭代器。它傳回一個迭代器,該迭代器產生包含來自每個輸入迭代器的元素的元組。

  • zip 的用法
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
    print(f'Name: {name}, Age: {age}')
  • zip 的注意事項

    • zip 函式在最短的輸入迭代器耗盡時停止。
    • 如果輸入迭代器的長度不一致,可以使用 itertools.zip_longest 函式。
  • 玄貓的建議

    使用 zip 函式可以簡潔地平行處理多個迭代器。

避免在 for 和 while 迴圈後使用 else 區塊:程式碼邏輯更清晰

在 Python 中,可以在 forwhile 迴圈後使用 else 區塊。else 區塊中的程式碼只有在迴圈正常結束時才會執行,也就是說,迴圈沒有被 break 陳述式中斷。

  • else 區塊的用法
for i in range(5):
    if i == 10:
        break
else:
    print('Loop completed successfully')
  • else 區塊的缺點

    • 容易被誤解。
    • 使程式碼更難以理解。
  • 玄貓的建議

    避免在 forwhile 迴圈後使用 else 區塊。如果需要執行只有在迴圈正常結束時才需要執行的程式碼,可以使用一個獨立的旗標變數。

善用 try/except/else/finally 中的每個區塊:錯誤處理的完整

try/except/else/finally 陳述式提供了一種完整的錯誤處理機制。每個區塊都有其特定的用途:

  • try:

    包含可能引發異常的程式碼。

  • except:

    包含用於處理特定型別異常的程式碼。

  • else:

    包含只有在 try 區塊中沒有引發異常時才會執行的程式碼。

  • finally:

    包含無論是否引發異常都會執行的程式碼。

  • 玄貓的建議

    善用 try/except/else/finally 陳述式中的每個區塊,可以編寫更健壯的程式碼。

重新認識 Python 的進階技巧:Metaclass、Concurrency、Built-in Modules 到 Production 佈署

本章探討 Python 的進階特性與實務應用,從 Metaclass 的靈活運用、Concurrency 的高效處理、Built-in Modules 的深度掌握,到 Production 環境的最佳化佈署,涵蓋了 Python 開發者在進階路上必須掌握的關鍵知識。以下玄貓將逐一解析各章節的重點,並分享玄貓在實務上的經驗與建議。

Metaclass 與 Attributes 的魔力:掌控 Python 的動態行為

Metaclass 和 dynamic attributes 是 Python 中極具威力的特性,它們賦予了開發者在執行時修改類別行為的能力。然而,這種強大的力量也伴隨著風險,若使用不當,可能導致程式碼難以理解和維護。玄貓建議,在使用這些特性時,務必遵循「最小驚奇原則」,確保程式碼的行為符合預期。

玄貓在過去的專案中,曾利用 Metaclass 實作一套 ORM (Object-Relational Mapping) 框架。透過 Metaclass,玄貓能夠在類別定義時自動建立資料函式庫表格的對應關係,簡化了資料函式庫操作的程式碼。然而,過度使用 Metaclass 也導致程式碼的複雜度提高,後續維護變得困難。因此,玄貓建議在決定使用 Metaclass 之前,務必仔細評估其必要性與潛在風險。

Concurrency 與 Parallelism:釋放 Python 的多核潛能

Python 提供了多種方式來實作 Concurrency (平行) 與 Parallelism (平行),讓程式能夠同時處理多個任務。然而,Python 的 GIL (Global Interpreter Lock) 限制了在單一 Python 行程中實作真正的平行運算。

玄貓在開發高流量網站時,就曾面臨 Concurrency 的挑戰。玄貓利用 asyncio 模組實作非同步 I/O,大幅提升了網站的吞吐量。然而,對於 CPU 密集型的任務,asyncio 的效果有限。此時,玄貓採用了 multiprocessing 模組,透過建立多個 Python 行程來實作平行運算,充分利用多核 CPU 的效能。

Built-in Modules:Python 的瑞士刀

Python 內建了許多實用的模組,涵蓋了各種常見的任務,例如檔案處理、網路通訊、資料序列化等。這些模組與 Python 的語法高度整合,是 Python 開發者不可或缺的工具。

玄貓認為,熟練掌握這些 Built-in Modules 是成為 Python 高手的必經之路。例如,玄貓經常使用 collections 模組中的 defaultdict 來簡化程式碼,使用 itertools 模組來處理迭代器,使用 json 模組來序列化和反序列化 JSON 資料。

Collaboration:開發可維護的 Python 專案

協作開發 Python 專案需要一套明確的規範與流程,以確保程式碼的品質與可維護性。玄貓建議,團隊成員應共同遵守 PEP 8 風格,並使用版本控制系統 (例如 Git) 來管理程式碼。

此外,玄貓也強調程式碼審查 (Code Review) 的重要性。透過程式碼審查,可以及早發現潛在的錯誤與風格問題,提升程式碼的品質。玄貓的團隊就採用了 GitHub 的 Pull Request 機制,要求所有程式碼變更都必須經過至少一位成員的審查。

Production:讓 Python 程式在真實世界中穩定執行

將 Python 程式佈署到 Production 環境需要考慮許多因素,例如效能、安全性、可維護性等。玄貓建議,應使用虛擬環境 (Virtual Environment) 來隔離專案的依賴套件,並使用日誌 (Logging) 來記錄程式的執行狀態。

此外,玄貓也強調單元測試 (Unit Test) 的重要性。透過單元測試,可以確保程式碼的各個部分都能夠正常運作。玄貓的團隊就採用了 pytest 框架,撰寫了大量的單元測試,確保程式碼的品質。

本章涵蓋了 Python 開發的各個導向,從基礎語法到進階技巧,從開發流程到佈署實務,為 Python 開發者提供了一份全面的。玄貓建議讀者在閱讀本章時,不僅要理解書中的概念,更要將其應用到實際專案中,才能真正掌握 Python 的精髓。

玄貓希望透過本文,能幫助讀者更深入地瞭解 Python 的進階技巧與實務應用。無論您是 Python 初學者還是資深開發者,都能從本章中獲得寶貴的知識與經驗。

為何我擁抱PEP 8:提升Python可讀性與協作效率

在Python的世界裡,風格不僅是個人偏好,更是協作的根本。玄貓在多年的Python開發生涯中,深刻體會到遵循PEP 8風格的重要性。它不僅提升了程式碼的可讀性,更促進了團隊協作效率。

擁抱Pythonic風格:簡潔、明確、可讀

Python社群推崇Pythonic風格,這是一種經驗累積的結晶,強調程式碼的簡潔、明確與可讀性。身為一位Python開發者,我認為理解並掌握Pythonic風格至關重要。

  • 明確勝於隱晦:程式碼應該直白易懂,避免過度複雜的邏輯。
  • 簡潔勝於複雜:用最少的程式碼完成任務,避免不必要的冗餘。
  • 可讀性至上:程式碼應該易於閱讀和理解,方便他人維護和修改。

Python版本選擇:導向未來,擁抱Python 3

目前Python 2和Python 3並存,但我強烈建議所有新的Python專案都應該使用Python 3。Python 3是Python社群的發展重點,擁有更多新特性和改進。

身為玄貓,我建議可透過以下方式查詢Python版本:

  1. 命令列查詢:在終端機輸入python --versionpython3 --version
  2. 程式碼查詢
import sys
print(sys.version_info)
print(sys.version)

PEP 8:Python程式碼的風格

PEP 8是Python官方的風格,它詳細描述瞭如何編寫清晰、易讀的Python程式碼。雖然遵循PEP 8不是強制性的,但我強烈建議所有Python開發者都應該遵守它。

1. 空白的使用

空白在Python中具有語法意義,因此正確使用空白至關重要。

  • 縮排:使用4個空格進行縮排,避免使用Tab。
  • 行長度:每行程式碼不應超過79個字元。
  • 空行:函式和類別之間使用兩個空行分隔,類別中的方法之間使用一個空行分隔。
  • 空格:避免在列表索引、函式呼叫或關鍵字引數指定時使用空格,在變數指定的前後各使用一個空格。
2. 命名規範

PEP 8對不同程式碼元素提出了不同的命名建議,有助於提高程式碼的可讀性。

  • 函式、變數和屬性:使用lowercase_underscore格式。
  • 受保護的例項屬性:使用_leading_underscore格式。
  • 私有例項屬性:使用__double_leading_underscore格式。
  • 類別和例外:使用CapitalizedWord格式。
  • 模組級別常數:使用ALL_CAPS格式。
  • 例項方法:使用self作為第一個引數的名稱。
  • 類別方法:使用cls作為第一個引數的名稱。
3. 表示式和陳述式

PEP 8提倡使用簡潔、明確的表示式和陳述式。

  • 行內否定:使用if a is not b代替if not a is b
  • 空值檢查:使用if not somelist代替if len(somelist) == 0
  • 單行陳述式:避免使用單行的if陳述式、for迴圈、while迴圈和except複合陳述式。
  • import陳述式:始終將import陳述式放在檔案的頂部,並按照標準函式庫模組、第三方模組、自定義模組的順序排列。
  • 絕對參照:始終使用絕對名稱參照模組,例如from bar import foo,而不是import foo

玄貓的建議

身為玄貓,我強烈建議所有Python開發者都應該學習並遵循PEP 8風格。它不僅可以提高程式碼的可讀性,還可以促進團隊協作效率。

總之,遵循PEP 8和Pythonic風格,是成為一位優秀Python開發者的重要一步。


### Python字串處理:玄貓的編碼解碼實戰

Python的世界裡,字串(String)是處理文字資料的核心。但你是否曾被`bytes``str``unicode`這些概念搞得一頭霧水?別擔心,玄貓將帶你撥開雲霧,理清Python 2Python 3中字串處理的差異,並分享一些實用的技巧,讓你在編碼解碼的路上不再迷茫。

### Python 2 vs Python 3:字串的二元宇宙

Python 2中,`str``unicode`是代表字元序列的兩種主要型別。`str`的例項包含原始的8位元值,而`unicode`的例項則包含Unicode字元

Python 3則有所不同,它使用`bytes``str`來代表字元序列。`bytes`包含原始的8位元值,`str`包含Unicode字元

這種差異是導致許多Python 2程式遷移到Python 3時出現問題的根源。

### 編碼與解碼:玄貓的UTF-8生存法則

Unicode字元可以透過多種方式表示為二進位資料(原始8位元值)。最常見的編碼方式是UTF-8。在Python 3中,`str`例項和Python 2中的`unicode`例項都沒有相關聯的二進位編碼。要將Unicode字元轉換為二進位資料,必須使用`encode`方法。要將二進位資料轉換為Unicode字元,必須使用`decode`方法。

玄貓建議,在編寫Python程式時,始終在介面的最外層進行Unicode的編碼和解碼。程式的核心應該使用Unicode字元型別Python 3中的`str`Python 2中的`unicode`),並且不應該對字元編碼做任何假設。這種方法可以讓你接受各種文字編碼(例如Latin-1Shift JIS和Big5),同時嚴格控制輸出文字編碼(理想情況下是UTF-8)。

### 轉換工具:玄貓的字串變形術

字元型別之間的區別導致了Python程式中兩種常見情況

1.  需要操作UTF-8編碼字元的原始8位元值(或其他編碼)。
2.  需要操作沒有特定編碼的Unicode字元

因此,我們經常需要兩個輔助函式在這兩種情況之間進行轉換,並確保輸入值的型別符合程式碼的預期。

**Python 3的轉換函式:**

```python
def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value  # Instance of str

def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        value = bytes_or_str.encode('utf-8')
    else:
        value = bytes_or_str
    return value  # Instance of bytes

內容解密:

  • to_str(bytes_or_str): 這個函式接收一個bytesstr型別的引數。如果輸入是bytes型別,它會使用UTF-8編碼將其解碼為str型別。如果輸入已經是str型別,則直接傳回。
  • to_bytes(bytes_or_str): 這個函式接收一個strbytes型別的引數。如果輸入是str型別,它會使用UTF-8編碼將其編碼為bytes型別。如果輸入已經是bytes型別,則直接傳回。

Python 2的轉換函式:

# Python 2
def to_unicode(unicode_or_str):
    if isinstance(unicode_or_str, str):
        value = unicode_or_str.decode('utf-8')
    else:
        value = unicode_or_str
    return value  # Instance of unicode

# Python 2
def to_str(unicode_or_str):
    if isinstance(unicode_or_str, unicode):
        value = unicode_or_str.encode('utf-8')
    else:
        value = unicode_or_str
    return value  # Instance of str

內容解密:

  • to_unicode(unicode_or_str): 這個函式接收一個strunicode型別的引數。如果輸入是str型別,它會使用UTF-8編碼將其解碼為unicode型別。如果輸入已經是unicode型別,則直接傳回。
  • to_str(unicode_or_str): 這個函式接收一個unicodestr型別的引數。如果輸入是unicode型別,它會使用UTF-8編碼將其編碼為str型別。如果輸入已經是str型別,則直接傳回。

踩坑經驗:玄貓的避雷

在Python中處理原始8位元值和Unicode字元時,有兩個主要的陷阱。

陷阱一:Python 2的假象

在Python 2中,當str僅包含7位元ASCII字元時,unicodestr例項似乎是相同的型別。

  • 可以使用+運算元將這樣的strunicode組合在一起。
  • 可以使用相等和不等運算元比較這樣的strunicode例項。
  • 可以使用unicode例項作為格式字串,例如'%s'

所有這些行為意味著你可以經常將strunicode例項傳遞給期望其中一個的函式,並且事情會正常運作(只要你只處理7位元ASCII)。在Python 3中,bytesstr例項永遠不相等,即使是空字串也不例外,因此你必須更加註意傳遞的字元序列的型別。

陷阱二:Python 3的預設編碼

在Python 3中,涉及檔案控制程式碼(由內建函式open傳回)的操作預設使用UTF-8編碼。在Python 2中,檔案操作預設使用二進位編碼。這會導致令人驚訝的失敗,尤其是對於習慣使用Python 2的程式設計師。

例如,假設你想將一些隨機二進位資料寫入檔案。在Python 2中,這可以正常工作。在Python 3中,這會當機。

import os

with open('/tmp/random.bin', 'w') as f:
    f.write(os.urandom(10))

內容解密:

這段程式碼在Python 2中可以正常執行,但在Python 3中會丟擲TypeError: must be str, not bytes異常。

這個異常的原因是在Python 3中為open新增了新的encoding引數。此引數預設為'utf-8'。這使得對檔案控制程式碼的讀寫操作期望str例項包含Unicode字元,而不是bytes例項包含二進位資料。

要使其正常工作,必須指示資料正在以二進位模式('wb')而不是字元模式('w')開啟。以下是在Python 2和Python 3中都能正確使用open的方式:

import os

with open('/tmp/random.bin', 'wb') as f:
    f.write(os.urandom(10))

內容解密:

透過將open函式的模式引數設定為'wb',我們告訴Python以二進位寫入模式開啟檔案。這意味著我們可以將bytes型別的資料直接寫入檔案,而無需進行編碼轉換。

讀取檔案資料時也存在此問題。解決方案是相同的:在開啟檔案時使用'rb'而不是'r'來指示二進位模式。