Python 的 Bool 和 Long 類別是其核心內建型別,理解它們的底層實作有助於掌握 Python 的運作方式。Bool 類別繼承自 Long 類別,並透過 Py_TruePy_False 兩個預定義常數實作布林值。Long 類別則使用 ob_digit 陣列儲存可變長度的數值,並透過 PyLong_FromLong 函式將 C 的長整數轉換為 Python 的 Long 物件。Unicode 字串在 Python 中至關重要,它支援多種編碼方式,例如 UTF-8 和 UTF-16,並使用 str 類別表示。處理 Unicode 字串時,encode()decode() 方法是關鍵,它們允許開發者在不同編碼間轉換。codecs 模組提供更廣泛的編碼支援,包括 ISO2022_JP,並允許增量編碼和解碼。理解 Python 的編碼和解碼機制對於處理文字資料至關重要,尤其是在國際化應用中。Python 的字典使用雜湊表實作快速鍵值對存取,所有不可變的內建型別都提供雜湊函式,確保物件生命週期內雜湊值不變,且值相同的不可變物件雜湊值相等。

屬性字典

屬性字典是一個包含類別方法、方法例項、類別屬性和例項屬性的字典。這個字典可以使用 __dict__() 函式存取。

Bool 和 Long 類別

Bool 類別是 Python 中最直接的內建類別實作。它繼承自 long 類別,並包含兩個預定義的常數:Py_TruePy_False。這些常數是在 Python 解譯器初始化時建立的。

Bool 類別實作

Bool 類別的實作位於 Objects/boolobject.c 檔案中。其中有一個輔助函式 PyBool_FromLong() 可以從一個長整數建立一個 Bool 例項。

PyObject *PyBool_FromLong(long ok)
{
    PyObject *result;

    if (ok)
        result = Py_True;
    else
        result = Py_False;
    Py_INCREF(result);
    return result;
}

這個函式使用 C 的數值型別計算來將 result 指派為 Py_TruePy_False,然後增加參照計數。

Long 類別實作

Long 類別的實作比 Bool 類別複雜一些。在 Python 3 中,Long 類別取代了 Int 類別,成為主要的整數類別。Long 類別可以儲存可變長度的值,最大長度由編譯的二進位制檔案決定。

Long 類別的資料結構由一個 PyObject 頭部和一個數字列表 ob_digit 組成。初始時,ob_digit 只包含一個數字,但可以在初始化時擴充套件。

struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

例如,數值 1 對應於 ob_digit[1],而數值 24,601 對應於 ob_digit[2, 4, 6, 0, 1]

為了將 C 的長整數轉換為 Python 的 Long 類別,需要先將 C 的長整數轉換為一個數字列表,然後為 Python 的 Long 物件分配記憶體,最後設定每個數字的值。

Mermaid 圖表
  classDiagram
    class PyObject {
        +PyObject_VAR_HEAD
        +digit ob_digit[1]
    }
    class PyLongObject {
        +PyLongObject()
        +PyLong_FromLong(long ival)
    }
    class PyBoolObject {
        +PyBool_FromLong(long ok)
    }
    PyObject <|-- PyLongObject
    PyObject <|-- PyBoolObject
    PyLongObject <|-- PyBoolObject

圖表翻譯

這個 Mermaid 圖表展示了 Python 中的物件和類別之間的關係。PyObject 是最基本的物件類別,包含了一個變數頭部和一個數字列表。PyLongObjectPyBoolObject 是繼承自 PyObject 的兩個子類別,分別代表 Long 和 Bool 類別。PyLongObject 有一個靜態方法 PyLong_FromLong() 可以從一個長整數建立一個 Long 例項,而 PyBoolObject 有一個靜態方法 PyBool_FromLong() 可以從一個長整數建立一個 Bool 例項。這個圖表展示了這些類別之間的繼承關係和方法呼叫關係。

Python 中的長整數實作

Python 的長整數(long)是透過 PyLongObject 來實作的。下面是 PyLong_FromLong 函式的實作,該函式用於將 C 的 long 整數轉換為 Python 的長整數:

if (v) {
    Py_SIZE(v) = sign;
    v->ob_digit[0] = Py_SAFE_DOWNCAST(
        abs_ival, unsigned long, digit);
}
return (PyObject*)v;

這個函式首先檢查 v 是否為空,如果不是空,則設定 Py_SIZE(v) 的值為 sign,並將 abs_ival 的值轉換為 digit 型別,然後指定給 v->ob_digit[0]

大數字的實作

對於大數字,Python 使用了一個迴圈來確定數字的位數。下面是相關程式碼:

t = abs_ival;
while (t) {
    ++ndigits;
    t >>= PyLong_SHIFT;
}
v = _PyLong_New(ndigits);

這個迴圈計算了大數字的位數,並建立了一個新的 PyLongObject 來儲存這個大數字。

長整數的比較

Python 的長整數比較是透過 long_richcompare 函式來實作的。這個函式是 long_compare 函式的包裝器。下面是相關程式碼:

static PyObject *
long_richcompare(PyObject *self, PyObject *other, int op)
{
    Py_ssize_t result;
    CHECK_BINOP(self, other);

    if (self == other)
        result = 0;
    else
        result = long_compare((PyLongObject*)self, (PyLongObject*)other);
    Py_RETURN_RICHCOMPARE(result, 0, op);
}

這個函式首先檢查兩個長整數是否相等,如果相等,則傳回 0。如果不相等,則呼叫 long_compare 函式來比較兩個長整數。

long_compare 函式

long_compare 函式用於比較兩個長整數。下面是相關程式碼:

long_compare() {
    //...
    if (a < b)
        return -1;
    else if (a == b)
        return 0;
    else
        return 1;
}

這個函式傳回一個整數值,表示兩個長整數的比較結果。如果 a 小於 b,則傳回 -1。如果 a 等於 b,則傳回 0。如果 a 大於 b,則傳回 1。

新增自定義比較邏輯

可以新增自定義比較邏輯,例如傳回 True,如果絕對值的結果小於或等於 1。下面是相關程式碼:

if (op == Py_AlE) {
    if (Py_ABS(result) <= 1)
        Py_RETURN_TRUE;
}

這個程式碼檢查如果運算子是 Py_AlE(即 “小於或等於”),則傳回 True,如果絕對值的結果小於或等於 1。

Python 中的 Unicode 字串

Python 中的 Unicode 字串是一種複雜的資料型態,支援多種編碼方式。以下是 Python 中 Unicode 字串的相關內容:

Unicode 簡介

Unicode 是一種標準的字元編碼系統,支援所有語言和字元的表示。Unicode 中的每個字元都有一個唯一的編碼點(code point),用於表示該字元。

Python 中的 Unicode 字串

Python 中的 Unicode 字串可以使用多種編碼方式,包括 UTF-8、UTF-16 和 UTF-32。UTF-8 是最常用的編碼方式,支援所有 Unicode 字元。

Unicode 編碼點

Unicode 編碼點是用於表示 Unicode 字元的唯一編碼。每個編碼點都有一個對應的字元。例如,U+00F7 是分號(÷)的編碼點,U+0107 是拉丁小寫字母 c WITHacute 的編碼點。

Python 中的 Unicode 字串表示

在 Python 中,Unicode 字串可以使用 \u 來表示。例如,\u0107 代表拉丁小寫字母 c WITHacute。

Unicode 相關檔案

以下是 Python 中與 Unicode 相關的檔案:

  • Include/unicodeobject.h:定義 Unicode 物件的結構。
  • Objects/unicodeobject.c:實作 Unicode 物件的功能。
  • Lib/encodings:包含所有可能的編碼方式。
  • Lib/codecs.py:實作 codecs 模組。
  • Modules/_codecsmodule.c:實作 codecs 模組的 C 擴充套件。

Unicode 處理

Python 不會嘗試將 Unicode 資料轉換為完整形式,因此如果您嘗試使用 \u107 來表示一個字元,將會引發一個異常。

UTF-8 和 UTF-16

UTF-8 和 UTF-16 是兩種最常用的 Unicode 編碼方式。UTF-8 是 8 位元的編碼方式,支援所有 Unicode 字元。UTF-16 是 16 位元的編碼方式,與 7 位元和 8 位元的編碼方式不相容。

Unicode 編碼與 Python

在 Python 中,Unicode 編碼是一個重要的概念,尤其是在處理國際化文字時。Unicode 是一個標準的字元編碼方案,能夠代表世界上絕大多數的語言和字元。

Unicode 基礎

Unicode 使用 16 位元(2 個 byte)的編碼方案來代表每個字元,這樣可以支援最多 65,536 個不同的字元。Unicode 編碼分為兩種:UTF-8 和 UTF-16。

UTF-8 是一種變長編碼方案,每個字元可以使用 1 到 4 個 byte 來表示。UTF-8 的優點是與 ASCII 相容,可以直接使用 ASCII 編碼的文字。

UTF-16 則是一種固定長度的編碼方案,每個字元使用 2 個 byte 來表示。UTF-16 的優點是可以直接存取每個字元,但需要考慮位元組順序(byte order)。

Python 中的 Unicode

在 Python 中,Unicode 字串使用 str 類別來表示。str 類別提供了許多方法來操作 Unicode 字串,例如 encode()decode()

encode() 方法可以將 Unicode 字串轉換為指定編碼的 byte 字串。例如:

>>> "hello".encode("utf-8")
b'hello'

decode() 方法可以將 byte 字串轉換為 Unicode 字串。例如:

>>> b"hello".decode("utf-8")
'hello'

Unicode 錯誤

在處理 Unicode 時,常會遇到錯誤。例如,當試圖將 byte 字串轉換為 Unicode 時,如果 byte 字串不符合指定編碼,會發生錯誤。例如:

>>> b"\xff".decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

解決 Unicode 錯誤

解決 Unicode 錯誤的方法包括:

  1. 使用正確的編碼:確保 byte 字串使用正確的編碼。
  2. 使用 errors 引數:在 decode() 方法中使用 errors 引數指定錯誤處理方式。例如:
>>> b"\xff".decode("utf-8", errors="ignore")
''
  1. 使用 replace 方法:使用 replace 方法替換無法解碼的 byte。例如:
>>> b"\xff".decode("utf-8", errors="replace")
'?'
內容解密:

在上述範例中,我們使用了 encode()decode() 方法來轉換 Unicode 字串和 byte 字串。這些方法需要指定編碼方式,例如 “utf-8” 或 “utf-16”。如果 byte 字串不符合指定編碼,會發生錯誤。

圖表翻譯:

以下是 Unicode 編碼的簡單圖表:

  graph LR
    A[Unicode 字串] -->|encode()|> B[byte 字串]
    B -->|decode()|> A
    B -->|errors="ignore"|> C[忽略錯誤]
    B -->|errors="replace"|> D[替換錯誤]

這個圖表顯示了 Unicode 字串和 byte 字串之間的轉換過程,以及錯誤處理方式。

ISO2022_JP 編碼與 Python 的 codecs 模組

Python 的 codecs 模組提供了對各種編碼的支援,包括 ISO2022_JP。這個模組允許開發者輕鬆地進行編碼和解碼的操作。

載入 ISO2022_JP 編碼

要使用 ISO2022_JP 編碼,首先需要載入 _codecs_iso2022 模組。這個模組定義了 ISO2022_JP 編碼的相關函式和類別。

import _codecs_iso2022

編碼和解碼

codecs 模組提供了 getencoder()getdecoder() 函式,分別用於取得編碼器和解碼器。這些函式可以根據指定的編碼名稱傳回相應的編碼器或解碼器。

iso2022_jp_encoder = codecs.getencoder('iso2022_jp')
iso2022_jp_decoder = codecs.getdecoder('iso2022_jp')

IncrementalEncoder 和 IncrementalDecoder

_codecs_iso2022 模組也定義了 IncrementalEncoderIncrementalDecoder 類別,這些類別繼承自 MultibyteIncrementalEncoderMultibyteIncrementalDecoder,並實作了編碼和解碼的增量操作。

class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder):
    codec = _codecs_iso2022.getcodec('iso2022_jp')

class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder):
    codec = _codecs_iso2022.getcodec('iso2022_jp')

編碼別名

Python 的 encodings 包含一個 aliases.py 模組,定義了一個別名字典,將編碼名稱對映到其別名。例如,utf8utf-8u8 都是 utf_8 編碼的別名。

codecs 模組的功能

codecs 模組不僅提供了編碼和解碼的功能,也實作了檔案操作的功能,包括開啟檔案描述符和讀寫檔案。

with codecs.open('example.txt', 'r', 'iso2022_jp') as file:
    content = file.read()

Unicode 物件的編碼方法

Python 的 Unicode 物件實作了多種編碼方法,包括 asciilatin1utf7 等。這些方法可以用於將 Unicode 字串轉換為指定編碼的位元組串。

unicode_string = u'Hello, World!'
ascii_bytes = unicode_string.encode('ascii')

內容解密:

上述程式碼展示瞭如何使用 Python 的 codecs 模組進行 ISO2022_JP 編碼和解碼的操作。透過載入 _codecs_iso2022 模組和使用 getencoder()getdecoder() 函式,可以取得 ISO2022_JP 編碼的編碼器和解碼器。另外,IncrementalEncoderIncrementalDecoder 類別提供了增量編碼和解碼的功能。最後,透過 codecs.open() 函式可以開啟檔案並進行讀寫操作。

圖表翻譯:

  flowchart TD
    A[開始] --> B[載入 _codecs_iso2022 模組]
    B --> C[取得 ISO2022_JP 編碼器和解碼器]
    C --> D[進行編碼和解碼操作]
    D --> E[使用 IncrementalEncoder 和 IncrementalDecoder 類別]
    E --> F[開啟檔案並進行讀寫操作]
    F --> G[結束]

圖表說明:

上述流程圖展示了使用 Python 的 codecs 模組進行 ISO2022_JP 編碼和解碼的步驟。從載入 _codecs_iso2022 模組開始,然後取得 ISO2022_JP 編碼器和解碼器,進行編碼和解碼操作,使用 IncrementalEncoderIncrementalDecoder 類別,最後開啟檔案並進行讀寫操作。

Python 中的編碼與解碼

Python 中的編碼與解碼是非常重要的概念,尤其是在處理字串和文字資料時。編碼是指將字串轉換為特定的格式,以便於儲存或傳輸,而解碼則是指將編碼後的資料轉換回原始的字串。

內建編碼

Python 中有一些內建的編碼,包括:

  • utf-8:是一種變長編碼,能夠表示所有 Unicode 字元。
  • utf-16:是一種固定長度的編碼,能夠表示所有 Unicode 字元。
  • utf-32:是一種固定長度的編碼,能夠表示所有 Unicode 字元。
  • unicode-escape:是一種特殊的編碼,能夠將 Unicode 字元轉換為 ASCII 字元。
  • raw-unicode-escape:是一種特殊的編碼,能夠將 Unicode 字元轉換為 ASCII 字元,且不進行任何轉義。

解碼方法

Python 中的解碼方法與編碼方法相似,但名稱中含有 Decode 而不是 Encode。例如:

  • utf-8 的解碼方法為 utf-8-decode
  • utf-16 的解碼方法為 utf-16-decode
  • utf-32 的解碼方法為 utf-32-decode

內部編碼

Python 中還有一些內部編碼,包括:

  • idna:實作了 RFC 3490 的 IDNA 編碼。
  • mbcs:在 Windows 平臺上,使用 ANSI 碼頁進行編碼。
  • raw-unicode-escape:將原始字串文字轉換為 Unicode 字串。
  • string-escape:將字串文字轉換為 Unicode 字串。
  • undefined:嘗試使用系統的預設編碼。

二進位制編碼

Python 中還有一些二進位制編碼,包括:

  • base64:將二進位制資料轉換為 MIME base64 格式。
  • bz2:使用 bz2 演算法對字串進行壓縮。
  • hex:將二進位制資料轉換為十六進製表示。

這些編碼和解碼方法可以使用 codecs 模組進行操作。例如:

import codecs

# 將字串轉換為 UTF-8 編碼
encoded_str = codecs.encode("hello world", "utf-8")

# 將 UTF-8 編碼的字串轉換回原始字串
decoded_str = codecs.decode(encoded_str, "utf-8")

print(decoded_str)  # 輸出:hello world

在這個例子中,我們使用 codecs.encode() 將字串 “hello world” 轉換為 UTF-8 編碼,然後使用 codecs.decode() 將 UTF-8 編碼的字串轉換回原始字串。

Unicode 字串比較

在 Python 中,Unicode 字串比較是一個重要的功能,尤其是在處理不同語言和編碼的文字時。這裡,我們將實作一個自定義的 Unicode 字串比較函式,該函式可以根據指定的比較運算子(如 ==!=<=>= 等)比較兩個 Unicode 字串。

基本概念

在 Python 中,Unicode 字串是使用 PyUnicodeObject 類別來表示的。當我們比較兩個 Unicode 字串時,Python 會呼叫 PyUnicode_RichCompare 函式,這個函式會根據指定的比較運算子傳回比較結果。

實作比較函式

下面是 PyUnicode_RichCompare 函式的一個簡化版本,該函式實作了對 Unicode 字串的比較:

PyObject *
PyUnicode_RichCompare(PyObject *left, PyObject *right, int op)
{
    //...

    if (left == right) {
        switch (op) {
            case Py_EQ:
            case Py_LE:
            case Py_GE:
                /* 字串相等 */
                Py_RETURN_TRUE;
            //...
        }
    }

    else if (op == Py_EQ || op == Py_NE) {
        /* 對字串進行大小寫不敏感比較 */
        PyObject *left_upper = PyUnicode_Upper(left);
        PyObject *right_upper = PyUnicode_Upper(right);

        int result = PyUnicode_Compare(left_upper, right_upper);

        Py_DECREF(left_upper);
        Py_DECREF(right_upper);

        if (op == Py_EQ) {
            return result == 0? Py_True : Py_False;
        } else {
            return result!= 0? Py_True : Py_False;
        }
    }

    //...
}

在上面的程式碼中,我們首先檢查兩個字串是否相等,如果相等,則直接傳回 Py_True。如果不相等,則根據指定的比較運算子進行比較。在這裡,我們實作了對字串的大小寫不敏感比較,首先將兩個字串轉換為大寫,然後進行比較。

測試和驗證

為了驗證上述實作的正確性,我們可以寫一些測試使用案例:

import unittest

class TestUnicodeCompare(unittest.TestCase):
    def test_eq(self):
        left = "Hello"
        right = "hello"
        self.assertTrue(PyUnicode_RichCompare(left, right, Py_EQ))

    def test_ne(self):
        left = "Hello"
        right = "world"
        self.assertTrue(PyUnicode_RichCompare(left, right, Py_NE))

if __name__ == "__main__":
    unittest.main()

透過這些測試使用案例,我們可以驗證 PyUnicode_RichCompare 函式的正確性。

瞭解Python中的字典和雜湊

Python中的字典(dictionary)是一種快速且靈活的對映方式,廣泛用於儲存和對映資料。它們也被用於Python物件的屬性和方法儲存,以及其他多種場景。

字典的特點

  • 快速: Python中的字典使用雜湊表(hash table)來儲存和檢索資料,這使得它們非常快。
  • 靈活: 字典可以用於儲存和對映各種型別的資料,包括字串、整數、浮點數等。
  • 緊湊: 字典只儲存鍵值對,這使得它們相對緊湊。

雜湊

所有不可變的內建型別都提供了雜湊函式。這個函式定義在型別的tp_hash槽或(對於自定義型別)透過特殊方法__hash__()。雜湊值的大小與指標相同(64位系統為64位,32位系統為32位),但它不代表值在記憶體中的地址。對於任何Python物件,其雜湊值在物件生命週期內不應改變。兩個不可變物件如果包含相同的值,則它們的雜湊值應相等。

從技術架構視角來看,Python 的核心資料結構,如字典、Bool、Long 和 Unicode 字串,展現了其設計哲學的精髓。字典以雜湊表為基礎,實作了高效的鍵值對存取,同時也支撐了 Python 物件的屬性系統。Bool 類別繼承自 Long 類別,並以簡潔的方式實作了布林邏輯。Long 類別則以可變長度的數字列表支援任意精確度的整數運算,有效解決了傳統整數型別溢位的問題。Unicode 字串的設計則體現了 Python 對國際化的重視,並透過 codecs 模組提供廣泛的編碼支援,簡化了多語言文字處理的複雜性。然而,Unicode 的複雜性也帶來了編碼錯誤的風險,需要開發者謹慎處理。

Python 的內建型別設計並非完美無缺。例如,Long 類別的變長設計雖然提升了精確度,但也可能導致效能瓶頸。Unicode 字串的編碼轉換也可能增加系統負擔。對於效能敏感的應用,需要仔細評估這些潛在影響。未來,Python 的核心資料結構可能朝向更進一步的效能最佳化和更便捷的 Unicode 處理發展。隨著 Python 生態的持續演進,我們預見會有更多工具和最佳實務出現,協助開發者更有效地運用這些核心資料結構,構建更強健、高效的應用程式。玄貓認為,深入理解這些核心資料結構的底層原理,對於 Python 開發者至關重要,這將有助於提升程式碼品質和效能,並更好地掌握 Python 語言的精髓。