簡介

對於 Python 的應用程式開發介面使得 C 和 C++ 開發者能夠在各種層級存取 Python 直譯器。該 API 同樣可用於 C++,但為簡潔起見,通常將其稱為 Python/C API。使用 Python/C API 有兩個不同的原因,第一個是為特定目的來編寫擴充模組;這些是擴充 Python 直譯器的 C 模組,這可能是最常見的用法。第二個原因是在更大的應用程式中將 Python 作為零件使用;這種技術通常在應用程式中稱為 embedding(嵌入式)Python。

編寫擴充模組是一個相對容易理解的過程,其中「食譜 (cookbook)」方法很有效。有幾種工具可以在一定程度上自動化該過程,儘管人們從早期就將 Python 嵌入到其他應用程式中,但嵌入 Python 的過程並不像編寫擴充那樣簡單。

不論你是嵌入還是擴充 Python,許多 API 函式都是很有用的;此外,大多數嵌入 Python 的應用程式也需要提供自定義擴充模組,因此在嘗試將 Python 嵌入實際應用程式之前熟悉編寫擴充可能是個好主意。

編寫標準

如果你正在編寫要引入於 CPython 中的 C 程式碼,你必須遵循 PEP 7 中定義的指南和標準。無論你貢獻的 Python 版本如何,這些指南都適用。對於你自己的第三方擴充模組,則不必遵循這些約定,除非你希望最終將它們貢獻給 Python。

引入檔案 (include files)

使用 Python/C API 所需的所有函式、型別和巨集的定義都透過以下這幾行來在你的程式碼中引入:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

這意味著會引入以下標準標頭:<stdio.h><string.h><errno.h><limits.h><assert.h><stdlib.h>(如果可用)。

備註

由於 Python 可能會定義一些會影響某些系統上標準標頭檔的預處理器 (pre-processor),因此你必須在引入任何標準標頭檔之前引入 Python.h

建議在引入 Python.h 之前都要定義 PY_SSIZE_T_CLEAN。有關此巨集的說明,請參閱剖析引數與建置數值

所有定義於 Python.h 中且使用者可見的名稱(另外透過標準標頭檔引入的除外)都具有 Py_Py 前綴。以 _Py 開頭的名稱供 Python 實作內部使用,擴充編寫者不應使用。結構成員名稱沒有保留前綴。

備註

使用者程式碼不應定義任何以 Py_Py 開頭的名稱。這會讓讀者感到困惑,並危及使用者程式碼在未來 Python 版本上的可移植性,這些版本可能會定義以這些前綴之一開頭的其他名稱。

標頭檔通常隨 Python 一起安裝。在 Unix 上它們位於目錄 prefix/include/pythonversion/exec_prefix/include/pythonversion/,其中 prefixexec_prefix 由 Python 的 configure 腳本的相應參數定義,version'%d.%d' % sys.version_info[:2]。在 Windows 上,標頭安裝在 prefix/include 中,其中 prefix 是指定給安裝程式 (installer) 用的安裝目錄。

要引入標頭,請將兩個(如果不同)目錄放在編譯器的引入搜索路徑 (search path) 中。不要將父目錄放在搜索路徑上,然後使用 #include <pythonX.Y/Python.h>;這會在多平台建置上壞掉,因為 prefix 下獨立於平台的標頭包括來自 exec_prefix 的平台特定標頭。

C++ 使用者應注意,儘管 API 完全使用 C 來定義,但標頭檔適當地將入口點聲明為 extern "C"。因此,無需執行任何特殊操作即可使用 C++ 中的 API。

有用的巨集

Python 標頭檔中定義了幾個有用的巨集,大多被定義在它們有用的地方附近(例如 Py_RETURN_NONE),其他是更通用的工具程式。以下並不一定是完整的列表。

PyMODINIT_FUNC

声明扩展模块 PyInit 初始化函数。 函数返回类型为 PyObject*。 该宏声明了平台所要求的任何特殊链接声明,并针对 C++ 将函数为声明为 extern "C"

初始化函数必须命名为 PyInit_name,其中 name 是模块名称,并且应为在模块文件中定义的唯一非 static 项。 例如:

static struct PyModuleDef spam_module = {
    PyModuleDef_HEAD_INIT,
    .m_name = "spam",
    ...
};

PyMODINIT_FUNC
PyInit_spam(void)
{
    return PyModule_Create(&spam_module);
}
Py_ABS(x)

回傳 x 的絕對值。

在 3.3 版新加入.

Py_ALWAYS_INLINE

要求編譯器總是嵌入靜態行內函式 (static inline function),編譯器可以忽略它並決定不嵌入該函式。

在禁用函式嵌入的除錯模式下建置 Python 時,它可用於嵌入有性能要求的靜態行內函式。例如,MSC 在除錯模式下建置時禁用函式嵌入。

盲目地使用 Py_ALWAYS_INLINE 標記靜態行內函式可能會導致更差的性能(例如程式碼大小增加)。在成本/收益分析方面,編譯器通常比開發人員更聰明。

如果 Python 是在除錯模式下建置(如果 Py_DEBUG 巨集有被定義),Py_ALWAYS_INLINE 巨集就什麼都不會做。

它必須在函式回傳型別之前被指定。用法:

static inline Py_ALWAYS_INLINE int random(void) { return 4; }

在 3.11 版新加入.

Py_CHARMASK(c)

引數必須是 [-128, 127] 或 [0, 255] 範圍內的字元或整數。這個巨集會將 c 轉換為 unsigned char 並回傳。

Py_DEPRECATED(version)

將其用於已棄用的聲明。巨集必須放在符號名稱之前。

範例:

Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);

在 3.8 版的變更: 新增了 MSVC 支援。

Py_GETENV(s)

類似於 getenv(s),但如果在命令列上傳遞了 -E 則回傳 NULL (請見 PyConfig.use_environment)。

Py_MAX(x, y)

回傳 xy 之間的最大值。

在 3.3 版新加入.

Py_MEMBER_SIZE(type, member)

以位元組為單位回傳結構 (type) member 的大小。

在 3.6 版新加入.

Py_MIN(x, y)

回傳 xy 之間的最小值。

在 3.3 版新加入.

Py_NO_INLINE

禁用函式的嵌入。例如,它減少了 C 堆疊的消耗:對大量嵌入程式碼的 LTO+PGO 建置很有用(請參閱 bpo-33720)。

用法:

Py_NO_INLINE static int random(void) { return 4; }

在 3.11 版新加入.

Py_STRINGIFY(x)

x 轉換為 C 字串。例如 Py_STRINGIFY(123) 會回傳 "123"

在 3.4 版新加入.

Py_UNREACHABLE()

當你的設計中有無法達到的程式碼路徑時,請使用此選項。例如在 case 語句已涵蓋了所有可能值的 switch 陳述式中的 default: 子句。在你可能想要呼叫 assert(0)abort() 的地方使用它。

在發布模式 (release mode) 下,巨集幫助編譯器最佳化程式碼,並避免有關無法存取程式碼的警告。例如該巨集是在發布模式下於 GCC 使用 __builtin_unreachable() 來實作。

Py_UNREACHABLE() 的一個用途是,在對一個永不回傳但並未聲明為 _Py_NO_RETURN 的函式之呼叫後使用。

如果程式碼路徑是極不可能但在特殊情況下可以到達,則不得使用此巨集。例如在低記憶體條件下或系統呼叫回傳了超出預期範圍的值。在這種情況下,最好將錯誤回報給呼叫者。如果無法回報錯誤則可以使用 Py_FatalError()

在 3.7 版新加入.

Py_UNUSED(arg)

將此用於函式定義中未使用的參數以消除編譯器警告。例如:int func(int a, int Py_UNUSED(b)) { return a; }

在 3.4 版新加入.

PyDoc_STRVAR(name, str)

建立一個名為 name 的變數,可以在文件字串中使用。如果 Python 是在沒有文件字串的情況下建置,則該值將為空。

PEP 7 中所指明,使用 PyDoc_STRVAR 作為文件字串可以支援在沒有文件字串的情況下建置 Python。

範例:

PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element.");

static PyMethodDef deque_methods[] = {
    // ...
    {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc},
    // ...
}
PyDoc_STR(str)

為給定的輸入字串建立一個文件字串,如果文件字串被禁用則建立空字串。

PEP 7 中所指明,使用 PyDoc_STR 指定文件字串以支援在沒有文件字串下建置 Python。

範例:

static PyMethodDef pysqlite_row_methods[] = {
    {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS,
        PyDoc_STR("Returns the keys of the row.")},
    {NULL, NULL}
};

物件、型別和參照計數

大多數 Python/C API 函式都有一個或多個引數以及一個型別為 PyObject* 的回傳值,此型別是一個指標,指向一個表示任意 Python 物件的晦暗 (opaque) 資料型別。由於在大多數情況下,Python 語言以相同的方式處理所有 Python 物件型別(例如賦值、作用域規則和引數傳遞),因此它們應該由單個 C 型別來表示。幾乎所有的 Python 物件都存在於堆積 (heap) 中:你永遠不會聲明 PyObject 型別的自動變數或靜態變數,只能聲明 PyObject* 型別的指標變數。唯一的例外是型別物件;由於它們絕不能被釋放,因此它們通常是靜態 PyTypeObject 物件。

所有 Python 物件(甚至是 Python 整數)都有一個型別 (type) 和一個參照計數 (reference count)。一個物件的型別決定了它是什麼種類的物件(例如一個整數、一個 list 或一個使用者定義的函式;還有更多型別,請見標準型別階層)。對於每個眾所周知的型別,都有一個巨集來檢查物件是否屬於該型別;例如,若(且唯若)*a* 指向的物件是 Python list 時,PyList_Check(a) 為真。

參照計數

參照計數很重要,因為現今的電腦記憶體大小是有限的(而且通常是非常有限的);它計算有多少個不同的地方用有了一個物件的強參照。這樣的地方可以是另一個物件,或者全域(或靜態)C 變數,或者某個 C 函式中的本地變數。當一個物件的最後一個強參照被釋放時(即其的參照計數變為零),該物件將被解除配置 (deallocated)。如果它包含對其他物件的參照,則它們的參照會被釋放。如果這樣的釋放使得再也沒有任何對於它們的參照,則可以依次為那些其他物件解除配置,依此類推。 (此處相互參照物件的存在是個明顯的問題;目前,解決方案是「就不要那樣做」。)

參照計數總是被明確地操作。正常的方法是使用巨集 Py_INCREF() 來取得對於物件的參照(即參照計數加一),並使用巨集 Py_DECREF() 來釋放參照(即將參照計數減一)。Py_DECREF() 巨集比 incref 巨集複雜得多,因為它必須檢查參照計數是否變為零,然後呼叫物件的釋放器 (deallocator)。釋放器是包含在物件型別結構中的函式指標。特定型別的釋放器,在如果是一個複合物件型別(例如 list)時負責釋放物件中包含的其他物件的參照,並執行任何需要的額外完結步驟。參照計數不可能溢出;至少與虛擬記憶體中用來保存參照計數的不同記憶體位置數量一樣多的位元會被使用(假設 sizeof(Py_ssize_t) >= sizeof(void*))。因此參照計數增加是一個簡單的操作。

沒有必要為每個包含物件指標的本地變數物件都持有一個強參照(即增加參照計數)。理論上,當變數指向它時,物件的參照計數會增加 1,而當變數離開作用域時就會減少 1。然而這兩者會相互抵消,所以最後參照計數沒有改變。使用參照計數的唯一真正原因是防止物件還有變數指向它時被解除配置。如果我們知道至少有一個物件的其他參照生存了至少與我們的變數一樣久,就不需要臨時增加建立新的強參照(即增加參照計數)。出現這種情況的一個重要情況是在從 Python 呼叫的擴充模組中作為引數傳遞給 C 函式的物件;呼叫機制保證在呼叫期間保持對每個參數的參照。

然而,一個常見的陷阱是從一個 list 中提取一個物件並保留它一段時間而不取得其參照。某些其他操作可能會從列表中刪除該物件,減少其參照計數並可能取消分配它。真正的危險是看似無害的操作可能會呼叫可以執行此操作的任意 Python 程式碼;有一個程式碼路徑允許控制權從 Py_DECREF() 回歸使用者,因此幾乎任何操作都有潛在危險。

一種安全的方法是都使用通用 (generics) 操作(名稱以 PyObject_PyNumber_PySequence_PyMapping_ 開頭的函式)。這些操作總是建立新的對於它們回傳物件的強參照(即增加其參照計數)。這讓呼叫者有責任在處理完結果後呼叫 Py_DECREF();這就成為第二本質。

參照計數詳細資訊

Python/C API 中函式的參照計數行為最好用參照的所有權來解釋。所有權附屬於參照而非物件(物件並非被擁有,它們總是共享的)。「擁有參照」意味著當不再需要該參照時,負責在其上呼叫 Py_DECREF。所有權也可以轉移,這意味著接收參照所有權的程式碼最終會負責在不需要參照時透過呼叫 Py_DECREF()Py_XDECREF() 釋放參照 --- 或者將這個責任再傳遞出去(通常是給它的呼叫者)。當一個函式將參照的所有權傳遞給它的呼叫者時,呼叫者被稱為接收到一個參照。當沒有所有權轉移時,呼叫者被稱為借用參照。如果是借用參照就不需要做任何事情。

相反地,當呼叫的函式傳入物件的參照時,有兩種可能性:函式有竊取 (steal) 物件的參照,或者沒有。 竊取參照意味著當你將參照傳遞給函式時,該函式假定它現在擁有該參照,並且你不再對它負責。

很少有函式會竊取參照;兩個值得注意的例外是 PyList_SetItem()PyTuple_SetItem(),它們竊取了對項目的參照(但不是對項目所在的 tuple 或 list 的參照!)。因為有著使用新建立的物件來增加 (populate) tuple 或 list 的習慣,這些函式旨在竊取參照;例如,建立 tuple (1, 2, "three") 的程式碼可以如下所示(先暫時忘記錯誤處理;更好的編寫方式如下所示):

PyObject *t;

t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyLong_FromLong(1L));
PyTuple_SetItem(t, 1, PyLong_FromLong(2L));
PyTuple_SetItem(t, 2, PyUnicode_FromString("three"));

這裡 PyLong_FromLong() 會回傳一個新的參照,它立即被 PyTuple_SetItem() 竊取。如果你想繼續使用一個物件,儘管對它的參照將被竊取,請在呼叫參照竊取函式之前使用 Py_INCREF() 來獲取另一個參照。

附帶地說,PyTuple_SetItem() 是設定 tuple 項目的唯一方法; PySequence_SetItem()PyObject_SetItem() 拒絕這樣做,因為 tuple 是一種不可變 (immutable) 的資料型別。你應該只對你自己建立的 tuple 使用 PyTuple_SetItem()

可以使用 PyList_New()PyList_SetItem() 編寫用於填充列表的等效程式碼。

但是在實際操作中你很少會使用這些方法來建立和增加 tuple 和 list。有一個通用函式 Py_BuildValue() 可以從 C 值建立最常見的物件,由 format string 引導。例如上面的兩個程式碼可以用以下程式碼替換(它還負責了錯誤檢查):

PyObject *tuple, *list;

tuple = Py_BuildValue("(iis)", 1, 2, "three");
list = Py_BuildValue("[iis]", 1, 2, "three");

更常見的是以那些借用參照的項目來使用 PyObject_SetItem() 及其系列函式,比如傳遞給你正在編寫的函式的引數。在那種情況下,他們關於參照的行為會比較穩健,因為你不取得新的一個參照就可以放棄參照(「讓它被竊取」)。例如,此函式將 list(實際上是任何可變序列)的所有項目設定於給定項目:

int
set_all(PyObject *target, PyObject *item)
{
    Py_ssize_t i, n;

    n = PyObject_Length(target);
    if (n < 0)
        return -1;
    for (i = 0; i < n; i++) {
        PyObject *index = PyLong_FromSsize_t(i);
        if (!index)
            return -1;
        if (PyObject_SetItem(target, index, item) < 0) {
            Py_DECREF(index);
            return -1;
        }
        Py_DECREF(index);
    }
    return 0;
}

函式回傳值的情況略有不同。雖然傳遞對大多數函式的參照不會改變你對該參照的所有權責任,但許多回傳物件參照的函式會給你該參照的所有權。原因很簡單:在很多情況下,回傳的物件是即時建立的,你獲得的參照是對該物件的唯一參照。因此回傳物件參照的通用函式,如 PyObject_GetItem()PySequence_GetItem(),總是回傳一個新的參照(呼叫者成為參照的所有者)。

重要的是要意識到你是否擁有一個函式回傳的參照只取決於你呼叫哪個函式 --- 羽毛 (plumage)*(作為引數傳遞給函式的物件之型別)*不會進入它!因此,如果你使用 PyList_GetItem() 從 list 中提取一個項目,你不會擁有其參照 --- 但如果你使用 PySequence_GetItem() 從同一 list 中獲取相同的項目(且恰好使用完全相同的引數),你確實會擁有對回傳物件的參照。

以下是一個範例,說明如何編寫函式來計算一個整數 list 中項目的總和;一次使用 PyList_GetItem(),一次使用 PySequence_GetItem()

long
sum_list(PyObject *list)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;

    n = PyList_Size(list);
    if (n < 0)
        return -1; /* Not a list */
    for (i = 0; i < n; i++) {
        item = PyList_GetItem(list, i); /* Can't fail */
        if (!PyLong_Check(item)) continue; /* Skip non-integers */
        value = PyLong_AsLong(item);
        if (value == -1 && PyErr_Occurred())
            /* Integer too big to fit in a C long, bail out */
            return -1;
        total += value;
    }
    return total;
}
long
sum_sequence(PyObject *sequence)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;
    n = PySequence_Length(sequence);
    if (n < 0)
        return -1; /* Has no length */
    for (i = 0; i < n; i++) {
        item = PySequence_GetItem(sequence, i);
        if (item == NULL)
            return -1; /* Not a sequence, or other failure */
        if (PyLong_Check(item)) {
            value = PyLong_AsLong(item);
            Py_DECREF(item);
            if (value == -1 && PyErr_Occurred())
                /* Integer too big to fit in a C long, bail out */
                return -1;
            total += value;
        }
        else {
            Py_DECREF(item); /* Discard reference ownership */
        }
    }
    return total;
}

型別

有少數幾個其他的資料型別在 Python/C API 中發揮重要作用;大多數是簡單的 C 型別,例如 intlongdoublechar*。一些結構型別被用於描述用於列出模組所匯出的函式或新物件型別的資料屬性的靜態表,其他則用於描述複數的值。這些將與使用它們的函式一起討論。

type Py_ssize_t
穩定 ABI 的一部分.

一個帶符號的整數型別,使得 sizeof(Py_ssize_t) == sizeof(size_t)。 C99 沒有直接定義這樣的東西(size_t 是無符號整數型別)。有關詳細資訊,請參閱 PEP 353PY_SSIZE_T_MAXPy_ssize_t 型別的最大正值。

例外

如果需要特定的錯誤處理,Python 開發者就只需要處理例外;未處理的例外會自動傳遞給呼叫者,然後傳遞給呼叫者的呼叫者,依此類推,直到它們到達頂層直譯器,在那裡它們透過堆疊回溯 (stack trace) 回報給使用者。

然而,對於 C 開發者來說,錯誤檢查總是必須是顯式的。除非在函式的文件中另有明確聲明,否則 Python/C API 中的所有函式都可以引發例外。通常當一個函式遇到錯誤時,它會設定一個例外,丟棄它擁有的任何物件參照,並回傳一個錯誤指示器。如果沒有另外文件記錄,這個指示器要麼是 NULL 不然就是 -1,取決於函式的回傳型別。有些函式會回傳布林值 true/false 結果,false 表示錯誤。很少有函式不回傳明確的錯誤指示器或者有不明確的回傳值,而需要使用 PyErr_Occurred() 明確測試錯誤。這些例外都會被明確地記錄於文件。

例外的狀態會在個別執行緒的存儲空間 (per-thread storage) 中維護(這相當於在非執行緒應用程式中使用全域存儲空間)。執行緒可以處於兩種狀態之一:發生例外或未發生例外。函式 PyErr_Occurred() 可用於檢查這一點:當例外發生時,它回傳對例外型別物件的借用參照,否則回傳 NULL。設定例外狀態的函式有很多:PyErr_SetString() 是最常見的(儘管不是最通用的)設定例外狀態的函式,而 PyErr_Clear() 是用來清除例外狀態。

完整的例外狀態由三個(都可以為 NULL 的)物件組成:例外型別、對應的例外值和回溯。這些與 sys.exc_info() 的 Python 結果具有相同的含義;但是它們並不相同:Python 物件表示由 Python try ... except 陳述式處理的最後一個例外,而 C 層級的例外狀態僅在例外在 C 函式間傳遞時存在,直到它到達 Python 位元組碼直譯器的主迴圈,該迴圈負責將它傳遞給 sys.exc_info() 和其系列函式。

請注意,從 Python 1.5 開始,從 Python 程式碼存取例外狀態的首選且支援執行緒安全的方法是呼叫 sys.exc_info() 函式,它回傳 Python 程式碼的個別執行緒例外狀態。此外,兩種存取例外狀態方法的語義都發生了變化,因此捕獲例外的函式將保存和恢復其執行緒的例外狀態,從而保留其呼叫者的例外狀態。這可以防止例外處理程式碼中的常見錯誤,這些錯誤是由看似無辜的函式覆蓋了正在處理的例外而引起的;它還替回溯中被堆疊幀 (stack frame) 參照的物件減少了通常不需要的生命週期延長。

作為一般原則,呼叫另一個函式來執行某些任務的函式應該檢查被呼叫函式是否引發了例外,如果是,則將例外狀態傳遞給它的呼叫者。它應該丟棄它擁有的任何物件參照,並回傳一個錯誤指示符,但它不應該設定另一個例外 --- 這將覆蓋剛剛引發的例外,並丟失關於錯誤確切原因的重要資訊。

上面的 sum_sequence() 範例展示了一個檢測例外並將其繼續傳遞的例子。碰巧這個例子在檢測到錯誤時不需要清理任何擁有的參照。以下範例函式展示了一些錯誤清理。首先,為了提醒你為什麼喜歡 Python,我們展示了等效的 Python 程式碼:

def incr_item(dict, key):
    try:
        item = dict[key]
    except KeyError:
        item = 0
    dict[key] = item + 1

這是相應的 C 程式碼:

int
incr_item(PyObject *dict, PyObject *key)
{
    /* Objects all initialized to NULL for Py_XDECREF */
    PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
    int rv = -1; /* Return value initialized to -1 (failure) */

    item = PyObject_GetItem(dict, key);
    if (item == NULL) {
        /* Handle KeyError only: */
        if (!PyErr_ExceptionMatches(PyExc_KeyError))
            goto error;

        /* Clear the error and use zero: */
        PyErr_Clear();
        item = PyLong_FromLong(0L);
        if (item == NULL)
            goto error;
    }
    const_one = PyLong_FromLong(1L);
    if (const_one == NULL)
        goto error;

    incremented_item = PyNumber_Add(item, const_one);
    if (incremented_item == NULL)
        goto error;

    if (PyObject_SetItem(dict, key, incremented_item) < 0)
        goto error;
    rv = 0; /* Success */
    /* Continue with cleanup code */

 error:
    /* Cleanup code, shared by success and failure path */

    /* Use Py_XDECREF() to ignore NULL references */
    Py_XDECREF(item);
    Py_XDECREF(const_one);
    Py_XDECREF(incremented_item);

    return rv; /* -1 for error, 0 for success */
}

這個例子代表了在 C 語言中對使用 goto 陳述句的認同!它闡述了以 PyErr_ExceptionMatches()PyErr_Clear() 來處理特定的例外,以及以 Py_XDECREF() 來配置其所擁有且可能為 NULL 的參照(注意名稱中的 'X'Py_DECREF() 在遇到 NULL 參照時會崩潰)。重要的是,用於保存擁有的參照的變數被初始化為 NULL 以使其能夠順利作用;同樣地,回傳值被初始化為 -1(失敗),並且僅在最後一次呼叫成功後才設定為成功。

嵌入式Python

只有 Python 直譯器的嵌入者(而不是擴充編寫者)需要擔心的一項重要任務是 Python 直譯器的初始化與完成階段。直譯器的大部分功能只能在直譯器初始化後使用。

基本的初始化函式是 Py_Initialize()。這會初始化帶有載入模組的表,並建立基礎模組 builtins__main__sys。它還會初始化模組搜索路徑 (sys.path)。

Py_Initialize() 不設定「腳本引數列表 (script argument list)」 (sys.argv)。如果稍後將要執行的 Python 程式碼需要此變數,則必須設定 PyConfig.argvPyConfig.parse_argv,請見 Python 初始化配置

在大多數系統上(特別是在 Unix 和 Windows 上,儘管細節略有不同),Py_Initialize() 會假設Python 函式庫相對於 Python 直譯器可執行檔案的位置固定,並根據其對標準 Python 直譯器可執行檔案位置的最佳猜測來計算模組搜索路徑。或者更詳細地說,它會在 shell 命令搜索路徑(環境變數 PATH)中找到名為 python 的可執行檔案,並在其父目錄中查找一個名為 lib/pythonX.Y 的目錄的相對位置。

例如,如果在 /usr/local/bin/python 中找到 Python 可執行檔案,它將假定函式庫位於 /usr/local/lib/pythonX.Y 中。 (事實上這個特定的路徑也是「後備 (fallback)」位置,當在 PATH 中找不到名為 python 的可執行檔案時使用。)使用者可以透過設定環境變數來覆蓋此行為 PYTHONHOME,或者透過設定 PYTHONPATH 在標準路徑前面插入額外的目錄。

嵌入的應用程式可以透過在呼叫 Py_Initialize() 之前呼叫 Py_SetProgramName(file) 來引導搜索。請注意 PYTHONHOME 仍然覆蓋它並且 PYTHONPATH 仍然插入在標準路徑的前面。需要完全控制權的應用程式必須實作自己的 Py_GetPath()Py_GetPrefix()Py_GetExecPrefix()Py_GetProgramFullPath()(全部定義在 Modules/getpath.c)。

有時會希望能夠「取消初始化 (uninitialize)」Python。例如,應用程式可能想要重新開始(再次呼叫 Py_Initialize())或者應用程式簡單地完成了對 Python 的使用並想要釋放 Python 分配的記憶體。這可以透過呼叫 Py_FinalizeEx() 來完成。如果 Python 當前處於初始化狀態,函式 Py_IsInitialized() 會回傳 true。有關這些功能的更多資訊將在後面的章節中給出。請注意 Py_FinalizeEx() 不會釋放由 Python 直譯器分配的所有記憶體,例如目前無法釋放被擴充模組所分配的記憶體。

除錯建置

Python 可以在建置時使用多個巨集來啟用對直譯器和擴充模組的額外檢查,這些檢查往往會在執行環境 (runtime) 增加大量開銷 (overhead),因此預設情況下不啟用它們。

Python 原始碼發佈版本中的 Misc/SpecialBuilds.txt 檔案有一份包含多種除錯構置的完整列表,為支援追蹤參照計數、為記憶體分配器除錯或對主直譯器迴圈進行低階分析的建置。本節的其餘部分將僅描述最常用的建置。

Py_DEBUG

使用定義的 Py_DEBUG 巨集編譯直譯器會生成 Python 的除錯建置Py_DEBUG 在 Unix 建置中要透過在 ./configure 命令中加入 --with-pydebug 來啟用。非 Python 限定的 _DEBUG 巨集的存在也暗示了這一點。當 Py_DEBUG 在 Unix 建置中啟用時,編譯器最佳化會被禁用。

除了下面描述的參照計數除錯之外,還會執行額外的檢查,請參閱 Python 除錯建置

定義 Py_TRACE_REFS 來啟用參照追蹤(參見調用 --with-trace-refs 選項)。當有定義時,透過向每個 PyObject 新增兩個額外欄位來維護有效物件的循環雙向鍊表 (circular doubly linked list)。全體分配也有被追蹤。退出時將印出所有現行參照。(在交互模式下,這發生在直譯器運行的每個陳述句之後。)

有關更多詳細資訊,請參閱 Python 原始碼發布版中的 Misc/SpecialBuilds.txt