From 41a8d1f199675987ae7ddad6308db83d26e0358d Mon Sep 17 00:00:00 2001 From: mattwang44 Date: Sat, 11 Apr 2026 22:17:13 +0800 Subject: [PATCH] Translate extending/extending.po --- extending/extending.po | 632 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 603 insertions(+), 29 deletions(-) diff --git a/extending/extending.po b/extending/extending.po index 154c37d8989..5aa985d9ca3 100644 --- a/extending/extending.po +++ b/extending/extending.po @@ -69,9 +69,9 @@ msgstr "" "C 擴充介面是 CPython 所特有的,擴充模組在其他 Python 實作上無法運作。在許多情" "況下,可以避免撰寫 C 擴充並保留對其他實作的可移植性。例如,如果你的用例是呼" "叫 C 函式庫函式或系統呼叫,你應該考慮使用 :mod:`ctypes` 模組或 `cffi " -"`_ 函式庫,而不是編寫自訂的 C 程式碼。這些模" -"組讓你可以撰寫 Python 程式碼來與 C 程式碼介接,而且比起撰寫和編譯 C 擴充模" -"組,這些模組在 Python 實作之間更容易移植。" +"`_ 函式庫,而不是編寫自訂的 C 程式碼。這些模組" +"讓你可以撰寫 Python 程式碼來與 C 程式碼介接,而且比起撰寫和編譯 C 擴充模組," +"這些模組在 Python 實作之間更容易移植。" #: ../../extending/extending.rst:40 msgid "A Simple Example" @@ -147,13 +147,15 @@ msgid "" msgstr "" "``#define PY_SSIZE_T_CLEAN`` 被用來表示在某些 API 中應該使用 ``Py_ssize_t`` " "而不是 ``int``。自 Python 3.13 起,它就不再是必要的了,但我們在此保留它以便向" -"後相容。關於這個巨集的描述請參閱 :ref:`arg-parsing-string-and-buffers`。" +"後相容。關於這個巨集的描述請參閱\\ :ref:`arg-parsing-string-and-buffers`。" #: ../../extending/extending.rst:77 msgid "" "All user-visible symbols defined by :file:`Python.h` have a prefix of ``Py`` " "or ``PY``, except those defined in standard header files." msgstr "" +"所有由 :file:`Python.h` 定義的使用者可見符號都具有 ``Py`` 或 ``PY`` 的前綴," +"在標準標頭檔中定義的除外。" #: ../../extending/extending.rst:82 msgid "" @@ -162,6 +164,9 @@ msgid "" "use, and should not rely on these implicit includes. If using the limited C " "API version 3.13 or newer, the implicit includes are:" msgstr "" +"為了向後相容,:file:`Python.h` 引入了數個標準標頭檔。C 擴充應該引入它們使用的" +"標準標頭檔,而不應依賴這些隱式的引入。如果使用限定 C API 版本 3.13 或更新版" +"本,隱式引入的有:" #: ../../extending/extending.rst:87 msgid "````" @@ -200,6 +205,8 @@ msgid "" "If :c:macro:`Py_LIMITED_API` is not defined, or is set to version 3.12 or " "older, the headers below are also included:" msgstr "" +"如果 :c:macro:`Py_LIMITED_API` 未被定義,或是被設定為版本 3.12 或更舊,則也會" +"引入以下標頭檔:" #: ../../extending/extending.rst:99 msgid "````" @@ -214,6 +221,8 @@ msgid "" "If :c:macro:`Py_LIMITED_API` is not defined, or is set to version 3.10 or " "older, the headers below are also included:" msgstr "" +"如果 :c:macro:`Py_LIMITED_API` 未被定義,或是被設定為版本 3.10 或更舊,則也會" +"引入以下標頭檔:" #: ../../extending/extending.rst:105 msgid "````" @@ -498,10 +507,12 @@ msgid "" "and initialize it by calling :c:func:`PyErr_NewException` in the module's :c:" "data:`Py_mod_exec` function (:c:func:`!spam_module_exec`)::" msgstr "" +"並透過在模組的 :c:data:`Py_mod_exec` 函式(:c:func:`!spam_module_exec`)中呼" +"叫 :c:func:`PyErr_NewException` 來初始化它: ::" #: ../../extending/extending.rst:240 msgid "SpamError = PyErr_NewException(\"spam.error\", NULL, NULL);" -msgstr "" +msgstr "SpamError = PyErr_NewException(\"spam.error\", NULL, NULL);" #: ../../extending/extending.rst:242 msgid "" @@ -509,12 +520,16 @@ msgid "" "every time the module is reinitialized, when the :c:data:`Py_mod_exec` " "function is called." msgstr "" +"由於 :c:data:`!SpamError` 是一個全域變數,每次模組被重新初始化、即 :c:data:" +"`Py_mod_exec` 函式被呼叫時,它都會被覆寫。" #: ../../extending/extending.rst:245 msgid "" "For now, let's avoid the issue: we will block repeated initialization by " "raising an :py:exc:`ImportError`::" msgstr "" +"目前,讓我們先避免這個問題:我們會透過引發 :py:exc:`ImportError` 來阻止重複初" +"始化: ::" #: ../../extending/extending.rst:248 msgid "" @@ -554,6 +569,41 @@ msgid "" " return PyModuleDef_Init(&spam_module);\n" "}" msgstr "" +"static PyObject *SpamError = NULL;\n" +"\n" +"static int\n" +"spam_module_exec(PyObject *m)\n" +"{\n" +" if (SpamError != NULL) {\n" +" PyErr_SetString(PyExc_ImportError,\n" +" \"cannot initialize spam module more than once\");\n" +" return -1;\n" +" }\n" +" SpamError = PyErr_NewException(\"spam.error\", NULL, NULL);\n" +" if (PyModule_AddObjectRef(m, \"SpamError\", SpamError) < 0) {\n" +" return -1;\n" +" }\n" +"\n" +" return 0;\n" +"}\n" +"\n" +"static PyModuleDef_Slot spam_module_slots[] = {\n" +" {Py_mod_exec, spam_module_exec},\n" +" {0, NULL}\n" +"};\n" +"\n" +"static struct PyModuleDef spam_module = {\n" +" .m_base = PyModuleDef_HEAD_INIT,\n" +" .m_name = \"spam\",\n" +" .m_size = 0, // 非負數\n" +" .m_slots = spam_module_slots,\n" +"};\n" +"\n" +"PyMODINIT_FUNC\n" +"PyInit_spam(void)\n" +"{\n" +" return PyModuleDef_Init(&spam_module);\n" +"}" #: ../../extending/extending.rst:284 msgid "" @@ -589,6 +639,9 @@ msgid "" "variable will not be garbage-collected. It will \"leak\". We did, however, " "ensure that this will happen at most once per process." msgstr "" +"目前,用來移除此參照的 :c:func:`Py_DECREF` 呼叫是缺失的。即使 Python 直譯器關" +"閉時,全域的 :c:data:`!SpamError` 變數也不會被垃圾回收。它會「洩漏」。然而," +"我們確實有確保這每個行程最多只會發生一次。" #: ../../extending/extending.rst:301 msgid "" @@ -678,7 +731,7 @@ msgid "" "it the string we just got from :c:func:`PyArg_ParseTuple`::" msgstr "" "接下來的陳述式會呼叫 Unix 函式 :c:func:`system`,並將剛才從 :c:func:" -"`PyArg_ParseTuple` 得到的字串傳給它:" +"`PyArg_ParseTuple` 得到的字串傳給它: ::" #: ../../extending/extending.rst:346 msgid "sts = system(command);" @@ -702,8 +755,8 @@ msgid "" "In this case, it will return an integer object. (Yes, even integers are " "objects on the heap in Python!)" msgstr "" -"在這種情況下它會回傳一個整數物件。(是的,在 Python 中連整數也是堆積 (heap) 上" -"的物件!)" +"在這種情況下它會回傳一個整數物件。(是的,在 Python 中連整數也是堆積 (heap) " +"上的物件!)" #: ../../extending/extending.rst:356 msgid "" @@ -812,6 +865,11 @@ msgid "" " ...\n" "};" msgstr "" +"static struct PyModuleDef spam_module = {\n" +" ...\n" +" .m_methods = spam_methods,\n" +" ...\n" +"};" #: ../../extending/extending.rst:408 msgid "" @@ -855,6 +913,10 @@ msgid "" "`PyModuleDef_Init`, so that the import machinery can create the module and " "store it in ``sys.modules``." msgstr "" +"當每個直譯器首次 import 其 :mod:`!spam` 模組時,就會呼叫 :c:func:`!" +"PyInit_spam`。(關於嵌入 Python 的說明請見下方。)必須透過 :c:func:" +"`PyModuleDef_Init` 回傳一個指向模組定義的指標,好讓 import 機制可以建立模組並" +"將其儲存在 ``sys.modules`` 中。" #: ../../extending/extending.rst:428 msgid "" @@ -980,6 +1042,12 @@ msgid "" "the module as having no support for subinterpreters (via :c:macro:" "`Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED`)." msgstr "" +"如果你宣告了一個全域變數或區域靜態變數,模組在重新初始化時可能會遇到非預期的" +"副作用,例如從 ``sys.modules`` 中移除條目或在一個行程內將已編譯的模組引入多個" +"直譯器中(或在沒有中間 :c:func:`exec` 的情況下進行 :c:func:`fork`)。如果模組" +"狀態尚未完全被\\ :ref:`隔離 `,作者應考慮將模組標" +"記為不支援子直譯器(透過 :c:macro:" +"`Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED`)。" #: ../../extending/extending.rst:492 msgid "" @@ -992,7 +1060,7 @@ msgstr "" #: ../../extending/extending.rst:500 msgid "Compilation and Linkage" -msgstr "" +msgstr "編譯與連結" #: ../../extending/extending.rst:502 msgid "" @@ -1003,6 +1071,10 @@ msgid "" "`building`) and additional information that pertains only to building on " "Windows (chapter :ref:`building-on-windows`) for more information about this." msgstr "" +"在你可以使用你的新擴充之前還有兩件事要做:編譯和連結它到 Python 系統。如果你" +"使用動態載入,細節可能取決於你系統所使用的動態載入方式;請參閱關於建置擴充模" +"組的章節(:ref:`building`)以及僅與在 Windows 上建置有關的額外資訊(:ref:" +"`building-on-windows`)以了解更多。" #: ../../extending/extending.rst:509 msgid "" @@ -1013,6 +1085,10 @@ msgid "" "the :file:`Modules/` directory of an unpacked source distribution, add a " "line to the file :file:`Modules/Setup.local` describing your file:" msgstr "" +"如果你無法使用動態載入,或者你想讓你的模組成為 Python 直譯器的永久部分,你就" +"必須更改組態設定並重新建置直譯器。幸運的是,在 Unix 上這非常簡單:只需將你的" +"檔案(例如 :file:`spammodule.c`)放在已解壓原始碼發行版本的 :file:`Modules/` " +"目錄中,在 :file:`Modules/Setup.local` 檔案中新增描述你的檔案的一列:" #: ../../extending/extending.rst:516 msgid "spam spammodule.o" @@ -1026,12 +1102,17 @@ msgid "" "running ':program:`make` Makefile'. (This is necessary each time you change " "the :file:`Setup` file.)" msgstr "" +"然後在最頂層目錄中執行 :program:`make` 來重新建置直譯器。你也可以在 :file:" +"`Modules/` 子目錄中執行 :program:`make`,但你必須先在那裡執行「:program:" +"`make` Makefile」來重新建置 :file:`Makefile`。(每次你修改 :file:`Setup` 檔案" +"時都需要這樣做。)" #: ../../extending/extending.rst:526 msgid "" "If your module requires additional libraries to link with, these can be " "listed on the line in the configuration file as well, for instance:" msgstr "" +"如果你的模組需要與額外的函式庫連結,這些也可以列在組態檔案中的該列上,例如:" #: ../../extending/extending.rst:529 msgid "spam spammodule.o -lX11" @@ -1039,7 +1120,7 @@ msgstr "spam spammodule.o -lX11" #: ../../extending/extending.rst:537 msgid "Calling Python Functions from C" -msgstr "" +msgstr "從 C 呼叫 Python 函式" #: ../../extending/extending.rst:539 msgid "" @@ -1051,6 +1132,10 @@ msgid "" "will require calling the Python callback functions from a C callback. Other " "uses are also imaginable." msgstr "" +"到目前為止,我們一直專注於讓 C 函式可以從 Python 呼叫。反過來也很有用:從 C " +"呼叫 Python 函式。對於支援所謂「回呼 (callback)」函式的函式庫尤其如此。如果一" +"個 C 介面使用了回呼,等價的 Python 通常就需要為 Python 程式設計者提供一個回呼" +"機制;其實作將需要從 C 回呼中呼叫 Python 回呼函式。其他用途也是可以想像的。" #: ../../extending/extending.rst:547 msgid "" @@ -1060,6 +1145,10 @@ msgid "" "interested, have a look at the implementation of the :option:`-c` command " "line option in :file:`Modules/main.c` from the Python source code.)" msgstr "" +"幸運的是,Python 直譯器可以很容易地被遞迴呼叫,並且有一個標準介面可以呼叫 " +"Python 函式。(我不會深入討論如何以特定字串作為輸入來呼叫 Python 剖析器 --- " +"如果你有興趣,可以查看 Python 原始碼中 :file:`Modules/main.c` 裡 :option:`-" +"c` 命令列選項的實作。)" #: ../../extending/extending.rst:553 msgid "" @@ -1070,6 +1159,11 @@ msgid "" "global variable --- or wherever you see fit. For example, the following " "function might be part of a module definition::" msgstr "" +"呼叫 Python 函式很容易。首先,Python 程式必須以某種方式將 Python 函式物件傳給" +"你。你應該提供一個函式(或其他介面)來做到這一點。當這個函式被呼叫時,將一個" +"指向 Python 函式物件的指標儲存在全域變數中(要注意對它呼叫 :c:func:" +"`Py_INCREF`!)--- 或任何你認為合適的地方。例如,以下函式可能是模組定義的一部" +"分: ::" #: ../../extending/extending.rst:560 msgid "" @@ -1097,6 +1191,29 @@ msgid "" " return result;\n" "}" msgstr "" +"static PyObject *my_callback = NULL;\n" +"\n" +"static PyObject *\n" +"my_set_callback(PyObject *dummy, PyObject *args)\n" +"{\n" +" PyObject *result = NULL;\n" +" PyObject *temp;\n" +"\n" +" if (PyArg_ParseTuple(args, \"O:set_callback\", &temp)) {\n" +" if (!PyCallable_Check(temp)) {\n" +" PyErr_SetString(PyExc_TypeError, \"parameter must be " +"callable\");\n" +" return NULL;\n" +" }\n" +" Py_XINCREF(temp); /* 為新的回呼增加一個參照 */\n" +" Py_XDECREF(my_callback); /* 釋放前一個回呼 */\n" +" my_callback = temp; /* 記住新的回呼 */\n" +" /* 回傳 \"None\" 的樣板程式碼 */\n" +" Py_INCREF(Py_None);\n" +" result = Py_None;\n" +" }\n" +" return result;\n" +"}" #: ../../extending/extending.rst:583 msgid "" @@ -1105,6 +1222,9 @@ msgid "" "c:func:`PyArg_ParseTuple` function and its arguments are documented in " "section :ref:`parsetuple`." msgstr "" +"這個函式必須使用 :c:macro:`METH_VARARGS` 旗標向直譯器註冊;這在 :ref:" +"`methodtable` 章節中有描述。:c:func:`PyArg_ParseTuple` 函式及其引數在 :ref:" +"`parsetuple` 章節中有文件說明。" #: ../../extending/extending.rst:588 msgid "" @@ -1113,6 +1233,9 @@ msgid "" "pointers (but note that *temp* will not be ``NULL`` in this context). More " "info on them in section :ref:`refcounts`." msgstr "" +"巨集 :c:func:`Py_XINCREF` 和 :c:func:`Py_XDECREF` 會遞增/遞減一個物件的參照" +"計數,而且在遇到 ``NULL`` 指標時是安全的(但請注意在這個情境中 *temp* 不會是 " +"``NULL``)。更多資訊請參閱\\ :ref:`refcounts`\\ 章節。" #: ../../extending/extending.rst:595 msgid "" @@ -1125,6 +1248,12 @@ msgid "" "func:`Py_BuildValue` returns a tuple when its format string consists of zero " "or more format codes between parentheses. For example::" msgstr "" +"稍後,當需要呼叫函式時,你呼叫 C 函式 :c:func:`PyObject_CallObject`。這個函式" +"有兩個引數,都是指向任意 Python 物件的指標:Python 函式和引數串列。引數串列必" +"須始終是一個元組物件,其長度就是引數的數量。要不帶引數呼叫 Python 函式,傳入 " +"``NULL`` 或一個空元組;要帶一個引數呼叫,傳入一個單元素元組。:c:func:" +"`Py_BuildValue` 會在其格式字串由括號之間的零個或多個格式碼組成時回傳一個元" +"組。例如: ::" #: ../../extending/extending.rst:604 msgid "" @@ -1139,6 +1268,16 @@ msgid "" "result = PyObject_CallObject(my_callback, arglist);\n" "Py_DECREF(arglist);" msgstr "" +"int arg;\n" +"PyObject *arglist;\n" +"PyObject *result;\n" +"...\n" +"arg = 123;\n" +"...\n" +"/* 是時候呼叫回呼了 */\n" +"arglist = Py_BuildValue(\"(i)\", arg);\n" +"result = PyObject_CallObject(my_callback, arglist);\n" +"Py_DECREF(arglist);" #: ../../extending/extending.rst:615 msgid "" @@ -1148,6 +1287,10 @@ msgid "" "new tuple was created to serve as the argument list, which is :c:func:" "`Py_DECREF`\\ -ed immediately after the :c:func:`PyObject_CallObject` call." msgstr "" +":c:func:`PyObject_CallObject` 會回傳一個 Python 物件指標:這是 Python 函式的" +"回傳值。:c:func:`PyObject_CallObject` 對其引數是「參照計數中性」的。在這個範" +"例中,一個新的元組被建立來作為引數串列,並在 :c:func:`PyObject_CallObject` 呼" +"叫之後立即被 :c:func:`Py_DECREF`。" #: ../../extending/extending.rst:622 msgid "" @@ -1157,6 +1300,10 @@ msgid "" "should somehow :c:func:`Py_DECREF` the result, even (especially!) if you are " "not interested in its value." msgstr "" +":c:func:`PyObject_CallObject` 的回傳值是「新的」:它要嘛是一個全新的物件,要" +"嘛是一個現有物件且其參照計數已被遞增。所以,除非你想將它儲存在全域變數中,否" +"則你應該以某種方式對結果呼叫 :c:func:`Py_DECREF`,即使(尤其是!)你對其值不" +"感興趣。" #: ../../extending/extending.rst:628 msgid "" @@ -1168,6 +1315,11 @@ msgid "" "handle the exception. If this is not possible or desirable, the exception " "should be cleared by calling :c:func:`PyErr_Clear`. For example::" msgstr "" +"然而在你這樣做之前,重要的是要檢查回傳值是否為 ``NULL``。如果是,Python 函式" +"是透過引發例外而終止的。如果呼叫 :c:func:`PyObject_CallObject` 的 C 程式碼是" +"從 Python 呼叫的,它現在應該回傳一個錯誤指示給它的 Python 呼叫者,這樣直譯器" +"就可以印出堆疊追蹤,或者呼叫端的 Python 程式碼可以處理例外。如果這是不可能或" +"不需要的,應該呼叫 :c:func:`PyErr_Clear` 來清除例外。例如: ::" #: ../../extending/extending.rst:636 msgid "" @@ -1176,6 +1328,10 @@ msgid "" "...use result...\n" "Py_DECREF(result);" msgstr "" +"if (result == NULL)\n" +" return NULL; /* 將錯誤傳回 */\n" +"...use result...\n" +"Py_DECREF(result);" #: ../../extending/extending.rst:641 msgid "" @@ -1188,6 +1344,11 @@ msgid "" "simplest way to do this is to call :c:func:`Py_BuildValue`. For example, if " "you want to pass an integral event code, you might use the following code::" msgstr "" +"根據 Python 回呼函式的預期介面,你可能還需要提供一個引數串列給 :c:func:" +"`PyObject_CallObject`。在某些情況下,引數串列也由 Python 程式透過指定回呼函式" +"的相同介面來提供。然後可以用與函式物件相同的方式來儲存和使用它。在其他情況" +"下,你可能需要建構一個新的元組來作為引數串列傳遞。最簡單的方式是呼叫 :c:func:" +"`Py_BuildValue`。例如,如果你想傳遞一個整數事件碼,你可以使用以下程式碼: ::" #: ../../extending/extending.rst:650 msgid "" @@ -1201,6 +1362,15 @@ msgid "" "/* Here maybe use the result */\n" "Py_DECREF(result);" msgstr "" +"PyObject *arglist;\n" +"...\n" +"arglist = Py_BuildValue(\"(l)\", eventcode);\n" +"result = PyObject_CallObject(my_callback, arglist);\n" +"Py_DECREF(arglist);\n" +"if (result == NULL)\n" +" return NULL; /* 將錯誤傳回 */\n" +"/* 這裡可以使用 result */\n" +"Py_DECREF(result);" #: ../../extending/extending.rst:660 msgid "" @@ -1209,6 +1379,9 @@ msgid "" "complete: :c:func:`Py_BuildValue` may run out of memory, and this should be " "checked." msgstr "" +"請注意 ``Py_DECREF(arglist)`` 是在呼叫之後、錯誤檢查之前立即放置的!另外請注" +"意嚴格來說這段程式碼並不完整::c:func:`Py_BuildValue` 可能會耗盡記憶體,這應" +"該要被檢查。" #: ../../extending/extending.rst:664 msgid "" @@ -1216,6 +1389,8 @@ msgid "" "`PyObject_Call`, which supports arguments and keyword arguments. As in the " "above example, we use :c:func:`Py_BuildValue` to construct the dictionary. ::" msgstr "" +"你也可以使用 :c:func:`PyObject_Call` 來呼叫帶有關鍵字引數的函式,它支援引數和" +"關鍵字引數。如同上面的範例,我們使用 :c:func:`Py_BuildValue` 來建構字典。 ::" #: ../../extending/extending.rst:668 msgid "" @@ -1229,18 +1404,27 @@ msgid "" "/* Here maybe use the result */\n" "Py_DECREF(result);" msgstr "" +"PyObject *dict;\n" +"...\n" +"dict = Py_BuildValue(\"{s:i}\", \"name\", val);\n" +"result = PyObject_Call(my_callback, NULL, dict);\n" +"Py_DECREF(dict);\n" +"if (result == NULL)\n" +" return NULL; /* 將錯誤傳回 */\n" +"/* 這裡可以使用 result */\n" +"Py_DECREF(result);" #: ../../extending/extending.rst:682 msgid "Extracting Parameters in Extension Functions" -msgstr "" +msgstr "擴充函式中的參數提取" #: ../../extending/extending.rst:686 msgid "The :c:func:`PyArg_ParseTuple` function is declared as follows::" -msgstr "" +msgstr ":c:func:`PyArg_ParseTuple` 函式的宣告如下: ::" #: ../../extending/extending.rst:688 msgid "int PyArg_ParseTuple(PyObject *arg, const char *format, ...);" -msgstr "" +msgstr "int PyArg_ParseTuple(PyObject *arg, const char *format, ...);" #: ../../extending/extending.rst:690 msgid "" @@ -1250,6 +1434,9 @@ msgid "" "Reference Manual. The remaining arguments must be addresses of variables " "whose type is determined by the format string." msgstr "" +"*arg* 引數必須是一個元組物件,包含從 Python 傳遞給 C 函式的引數串列。" +"*format* 引數必須是一個格式字串,其語法在 Python/C API 參考手冊中的\\ :ref:" +"`arg-parsing`\\ 有說明。其餘引數必須是變數的位址,其型別由格式字串決定。" #: ../../extending/extending.rst:696 msgid "" @@ -1258,12 +1445,17 @@ msgid "" "variables passed to the call: if you make mistakes there, your code will " "probably crash or at least overwrite random bits in memory. So be careful!" msgstr "" +"請注意,雖然 :c:func:`PyArg_ParseTuple` 會檢查 Python 引數是否具有所需的型" +"別,但它無法檢查傳遞給呼叫的 C 變數位址是否有效:如果你在那裡犯了錯誤,你的程" +"式碼可能會崩潰,或至少覆寫記憶體中的隨機位元。所以要小心!" #: ../../extending/extending.rst:701 msgid "" "Note that any Python object references which are provided to the caller are " "*borrowed* references; do not decrement their reference count!" msgstr "" +"請注意,提供給呼叫者的任何 Python 物件參照都是\\ *借用*\\ 參照;不要遞減它們" +"的參照計數!" #: ../../extending/extending.rst:704 msgid "Some example calls::" @@ -1294,6 +1486,8 @@ msgid "" "ok = PyArg_ParseTuple(args, \"s\", &s); /* A string */\n" " /* Possible Python call: f('whoops!') */" msgstr "" +"ok = PyArg_ParseTuple(args, \"s\", &s); /* 一個字串 */\n" +" /* 可能的 Python 呼叫:f('whoops!') */" #: ../../extending/extending.rst:727 msgid "" @@ -1301,6 +1495,9 @@ msgid "" "*/\n" " /* Possible Python call: f(1, 2, 'three') */" msgstr "" +"ok = PyArg_ParseTuple(args, \"lls\", &k, &l, &s); /* 兩個 long 和一個字串 " +"*/\n" +" /* 可能的 Python 呼叫:f(1, 2, 'three') */" #: ../../extending/extending.rst:732 msgid "" @@ -1308,6 +1505,9 @@ msgid "" " /* A pair of ints and a string, whose size is also returned */\n" " /* Possible Python call: f((1, 2), 'three') */" msgstr "" +"ok = PyArg_ParseTuple(args, \"(ii)s#\", &i, &j, &s, &size);\n" +" /* 一對 int 和一個字串,其大小也會被回傳 */\n" +" /* 可能的 Python 呼叫:f((1, 2), 'three') */" #: ../../extending/extending.rst:738 msgid "" @@ -1323,6 +1523,17 @@ msgid "" " f('spam', 'wb', 100000) */\n" "}" msgstr "" +"{\n" +" const char *file;\n" +" const char *mode = \"r\";\n" +" int bufsize = 0;\n" +" ok = PyArg_ParseTuple(args, \"s|si\", &file, &mode, &bufsize);\n" +" /* 一個字串,以及可選的另一個字串和一個整數 */\n" +" /* 可能的 Python 呼叫:\n" +" f('spam')\n" +" f('spam', 'w')\n" +" f('spam', 'wb', 100000) */\n" +"}" #: ../../extending/extending.rst:752 msgid "" @@ -1335,6 +1546,14 @@ msgid "" " f(((0, 0), (400, 300)), (10, 10)) */\n" "}" msgstr "" +"{\n" +" int left, top, right, bottom, h, v;\n" +" ok = PyArg_ParseTuple(args, \"((ii)(ii))(ii)\",\n" +" &left, &top, &right, &bottom, &h, &v);\n" +" /* 一個矩形和一個點 */\n" +" /* 可能的 Python 呼叫:\n" +" f(((0, 0), (400, 300)), (10, 10)) */\n" +"}" #: ../../extending/extending.rst:763 msgid "" @@ -1345,15 +1564,21 @@ msgid "" " /* Possible Python call: myfunction(1+2j) */\n" "}" msgstr "" +"{\n" +" Py_complex c;\n" +" ok = PyArg_ParseTuple(args, \"D:myfunction\", &c);\n" +" /* 一個複數,同時也提供函式名稱以便產生錯誤訊息 */\n" +" /* 可能的 Python 呼叫:myfunction(1+2j) */\n" +"}" #: ../../extending/extending.rst:774 msgid "Keyword Parameters for Extension Functions" -msgstr "" +msgstr "擴充函式的關鍵字參數" #: ../../extending/extending.rst:778 msgid "" "The :c:func:`PyArg_ParseTupleAndKeywords` function is declared as follows::" -msgstr "" +msgstr ":c:func:`PyArg_ParseTupleAndKeywords` 函式的宣告如下: ::" #: ../../extending/extending.rst:780 msgid "" @@ -1376,6 +1601,11 @@ msgid "" "`PyArg_ParseTupleAndKeywords` returns true, otherwise it returns false and " "raises an appropriate exception." msgstr "" +"*arg* 和 *format* 參數與 :c:func:`PyArg_ParseTuple` 函式的相同。*kwdict* 參數" +"是從 Python runtime 接收到的第三個參數,即關鍵字的字典。*kwlist* 參數是一個" +"以 ``NULL`` 終止的字串 list,用於識別各參數;這些名稱會從左到右與 *format* 中" +"的型別資訊進行配對。成功時,:c:func:`PyArg_ParseTupleAndKeywords` 會回傳 " +"true,否則回傳 false 並引發適當的例外。" #: ../../extending/extending.rst:793 msgid "" @@ -1383,12 +1613,16 @@ msgid "" "parameters passed in which are not present in the *kwlist* will cause :exc:" "`TypeError` to be raised." msgstr "" +"使用關鍵字引數時無法剖析巢狀的 tuple!傳入的關鍵字參數如果不在 *kwlist* 中," +"將會引發 :exc:`TypeError`。" #: ../../extending/extending.rst:799 msgid "" "Here is an example module which uses keywords, based on an example by Geoff " "Philbrick (philbrick@hks.com)::" msgstr "" +"以下是一個使用關鍵字的範例模組,基於 Geoff Philbrick (philbrick@hks.com) 的範" +"例: ::" #: ../../extending/extending.rst:802 msgid "" @@ -1442,16 +1676,65 @@ msgid "" " return PyModuleDef_Init(&keywdarg_module);\n" "}" msgstr "" +"#define PY_SSIZE_T_CLEAN\n" +"#include \n" +"\n" +"static PyObject *\n" +"keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)\n" +"{\n" +" int voltage;\n" +" const char *state = \"a stiff\";\n" +" const char *action = \"voom\";\n" +" const char *type = \"Norwegian Blue\";\n" +"\n" +" static char *kwlist[] = {\"voltage\", \"state\", \"action\", \"type\", " +"NULL};\n" +"\n" +" if (!PyArg_ParseTupleAndKeywords(args, keywds, \"i|sss\", kwlist,\n" +" &voltage, &state, &action, &type))\n" +" return NULL;\n" +"\n" +" printf(\"-- This parrot wouldn't %s if you put %i Volts through it." +"\\n\",\n" +" action, voltage);\n" +" printf(\"-- Lovely plumage, the %s -- It's %s!\\n\", type, state);\n" +"\n" +" Py_RETURN_NONE;\n" +"}\n" +"\n" +"static PyMethodDef keywdarg_methods[] = {\n" +" /* 函式的轉型是必要的,因為 PyCFunction 值\n" +" * 只接受兩個 PyObject* 參數,而 keywdarg_parrot() 接受\n" +" * 三個。\n" +" */\n" +" {\"parrot\", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | " +"METH_KEYWORDS,\n" +" \"Print a lovely skit to standard output.\"},\n" +" {NULL, NULL, 0, NULL} /* 哨兵 */\n" +"};\n" +"\n" +"static struct PyModuleDef keywdarg_module = {\n" +" .m_base = PyModuleDef_HEAD_INIT,\n" +" .m_name = \"keywdarg\",\n" +" .m_size = 0,\n" +" .m_methods = keywdarg_methods,\n" +"};\n" +"\n" +"PyMODINIT_FUNC\n" +"PyInit_keywdarg(void)\n" +"{\n" +" return PyModuleDef_Init(&keywdarg_module);\n" +"}" #: ../../extending/extending.rst:853 msgid "Building Arbitrary Values" -msgstr "" +msgstr "建構任意值" #: ../../extending/extending.rst:855 msgid "" "This function is the counterpart to :c:func:`PyArg_ParseTuple`. It is " "declared as follows::" -msgstr "" +msgstr "此函式是 :c:func:`PyArg_ParseTuple` 的對應函式。它的宣告如下: ::" #: ../../extending/extending.rst:858 msgid "PyObject *Py_BuildValue(const char *format, ...);" @@ -1464,6 +1747,9 @@ msgid "" "not output) must not be pointers, just values. It returns a new Python " "object, suitable for returning from a C function called from Python." msgstr "" +"它能辨識一組與 :c:func:`PyArg_ParseTuple` 所辨識的類似的格式單元,但引數(是" +"函式的輸入,而非輸出)不能是指標,只能是值。它會回傳一個新的 Python 物件,適" +"合從被 Python 呼叫的 C 函式中回傳。" #: ../../extending/extending.rst:865 msgid "" @@ -1476,11 +1762,17 @@ msgid "" "that format unit. To force it to return a tuple of size 0 or one, " "parenthesize the format string." msgstr "" +"與 :c:func:`PyArg_ParseTuple` 的一個區別是:後者要求它的第一個引數是一個 " +"tuple(因為 Python 引數 list 在內部總是以 tuple 來表示),而 :c:func:" +"`Py_BuildValue` 並不總是建構一個 tuple。只有當格式字串包含兩個或更多格式單元" +"時,它才會建構一個 tuple。如果格式字串為空,它會回傳 ``None``;如果格式字串恰" +"好包含一個格式單元,它會回傳該格式單元所描述的任何物件。要強制它回傳大小為 0 " +"或 1 的 tuple,請將格式字串加上括號。" #: ../../extending/extending.rst:873 msgid "" "Examples (to the left the call, to the right the resulting Python value):" -msgstr "" +msgstr "範例(左邊是呼叫,右邊是結果的 Python 值):" #: ../../extending/extending.rst:875 msgid "" @@ -1532,6 +1824,9 @@ msgid "" "``new`` and ``delete`` are used with essentially the same meaning and we'll " "restrict the following discussion to the C case." msgstr "" +"在像 C 或 C++ 這類語言中,程式設計師負責在堆積上動態配置和釋放記憶體。在 C " +"中,這是使用 :c:func:`malloc` 和 :c:func:`free` 函式來完成的。在 C++ 中,運算" +"子 ``new`` 和 ``delete`` 的意義基本相同,我們將以下的討論限制在 C 的情況。" #: ../../extending/extending.rst:907 msgid "" @@ -1547,6 +1842,14 @@ msgid "" "as referencing uninitialized data --- core dumps, wrong results, mysterious " "crashes." msgstr "" +"每個使用 :c:func:`malloc` 配置的記憶體區塊,最終都應該透過恰好一次的 :c:func:" +"`free` 呼叫回歸到可用記憶體池中。在正確的時機呼叫 :c:func:`free` 很重要。如果" +"一個區塊的位址被遺忘了但沒有對它呼叫 :c:func:`free`,那麼它佔用的記憶體在程式" +"終止之前都無法被重新使用。這稱為\\ :dfn:`記憶體洩漏 (memory leak)`。另一方" +"面,如果程式對一個區塊呼叫了 :c:func:`free` 之後又繼續使用該區塊,這會與透過" +"另一個 :c:func:`malloc` 呼叫重新使用該區塊產生衝突。這稱為\\ :dfn:`使用已釋放" +"的記憶體 (using freed memory)`。它的後果和引用未初始化的資料一樣糟糕 --- " +"core dump、錯誤的結果、神秘的當機。" #: ../../extending/extending.rst:918 msgid "" @@ -1564,6 +1867,14 @@ msgid "" "happening by having a coding convention or strategy that minimizes this kind " "of errors." msgstr "" +"記憶體洩漏的常見原因是程式碼中不尋常的路徑。例如,一個函式可能配置一塊記憶" +"體、做一些計算,然後再次釋放該區塊。現在,對函式需求的變更可能會在計算中加入" +"一個測試來偵測錯誤條件,並可以從函式中提前回傳。當走這條提前退出的路徑時,很" +"容易忘記釋放已配置的記憶體區塊,特別是當它是後來才加入程式碼中的。這種洩漏一" +"旦引入,通常會在很長一段時間內都不被發現:錯誤退出只在所有呼叫中的一小部分發" +"生,而且大多數現代機器有充足的虛擬記憶體,所以洩漏只有在頻繁使用有洩漏函式的" +"長時間執行程序中才會變得明顯。因此,透過採用能最小化這類錯誤的編碼慣例或策略" +"來防止洩漏發生是很重要的。" #: ../../extending/extending.rst:931 msgid "" @@ -1575,6 +1886,11 @@ msgid "" "reference to it is deleted. When the counter reaches zero, the last " "reference to the object has been deleted and the object is freed." msgstr "" +"由於 Python 大量使用 :c:func:`malloc` 和 :c:func:`free`,它需要一個策略來避免" +"記憶體洩漏以及使用已釋放記憶體的問題。所選擇的方法稱為\\ :dfn:`參照計數 " +"(reference counting)`。原理很簡單:每個物件包含一個計數器,當一個指向該物件的" +"參照被儲存在某處時計數器遞增,當一個指向它的參照被刪除時計數器遞減。當計數器" +"歸零時,表示該物件的最後一個參照已被刪除,物件便被釋放。" #: ../../extending/extending.rst:939 msgid "" @@ -1591,6 +1907,13 @@ msgid "" "garbage collector will be available for C. Until then, we'll have to live " "with reference counts." msgstr "" +"另一種替代策略稱為\\ :dfn:`自動垃圾回收 (automatic garbage collection)`。(有" +"時,參照計數也被稱為一種垃圾回收策略,因此我使用「自動」來區分兩者。)自動垃" +"圾回收的最大優點是使用者不需要明確地呼叫 :c:func:`free`。(另一個聲稱的優點是" +"速度或記憶體使用量的改善 --- 但這並非確定的事實。)缺點是對於 C 語言而言,並" +"沒有真正可移植的自動垃圾回收器,而參照計數可以被可移植地實作(只要 :c:func:" +"`malloc` 和 :c:func:`free` 函式可用 --- 這是 C 標準所保證的)。也許有一天會有" +"足夠可移植的 C 自動垃圾回收器可用。在那之前,我們只能與參照計數共存。" #: ../../extending/extending.rst:951 msgid "" @@ -1605,6 +1928,12 @@ msgid "" "in a reference cycle, or referenced from the objects in the cycle, even " "though there are no further references to the cycle itself." msgstr "" +"雖然 Python 使用傳統的參照計數實作,但它也提供了一個能偵測參照循環的循環偵測" +"器。這讓應用程式不必擔心建立直接或間接的循環參照;這些是僅使用參照計數來實作" +"垃圾回收的弱點。參照循環由包含指向自身的(可能是間接的)參照的物件組成,使得" +"循環中的每個物件的參照計數都不為零。典型的參照計數實作無法回收屬於參照循環中" +"的任何物件的記憶體,或從循環中的物件所參照的記憶體,即使除了循環本身之外已經" +"沒有其他的參照了。" #: ../../extending/extending.rst:962 msgid "" @@ -1613,6 +1942,8 @@ msgid "" "collect` function), as well as configuration interfaces and the ability to " "disable the detector at runtime." msgstr "" +"循環偵測器能夠偵測垃圾循環並回收它們。:mod:`gc` 模組公開了一種執行偵測器的方" +"式(:func:`~gc.collect` 函式),以及配置介面和在 runtime 停用偵測器的功能。" #: ../../extending/extending.rst:971 msgid "Reference Counting in Python" @@ -1628,6 +1959,10 @@ msgid "" "this purpose (and others), every object also contains a pointer to its type " "object." msgstr "" +"有兩個巨集,``Py_INCREF(x)`` 和 ``Py_DECREF(x)``,用於處理參照計數的遞增和遞" +"減。:c:func:`Py_DECREF` 也會在計數歸零時釋放物件。為了彈性,它不會直接呼叫 :" +"c:func:`free` --- 而是透過物件的\\ :dfn:`型別物件 (type object)` 中的函式指標" +"進行呼叫。為此目的(以及其他目的),每個物件也包含一個指向其型別物件的指標。" #: ../../extending/extending.rst:980 msgid "" @@ -1641,6 +1976,12 @@ msgid "" "on, store it, or call :c:func:`Py_DECREF`. Forgetting to dispose of an owned " "reference creates a memory leak." msgstr "" +"現在剩下的大問題是:何時使用 ``Py_INCREF(x)`` 和 ``Py_DECREF(x)``?讓我們先介" +"紹一些術語。沒有人「擁有」一個物件;然而,你可以\\ :dfn:`擁有一個參照 (own a " +"reference)` 到一個物件。一個物件的參照計數現在被定義為指向它的被擁有參照的數" +"量。參照的擁有者負責在不再需要該參照時呼叫 :c:func:`Py_DECREF`。參照的擁有權" +"可以被轉移。處置一個被擁有參照有三種方式:傳遞它、儲存它、或呼叫 :c:func:" +"`Py_DECREF`。忘記處置一個被擁有的參照會造成記憶體洩漏。" #: ../../extending/extending.rst:989 msgid "" @@ -1650,6 +1991,9 @@ msgid "" "borrowed. Using a borrowed reference after the owner has disposed of it " "risks using freed memory and should be avoided completely [#]_." msgstr "" +"也可以\\ :dfn:`借用 (borrow)` [#]_ 一個物件的參照。借用參照的人不應呼叫 :c:" +"func:`Py_DECREF`。借用者持有物件的時間不能超過借出參照的擁有者。在擁有者已經" +"處置了參照之後使用借用參照,會有使用已釋放記憶體的風險,應該完全避免 [#]_。" #: ../../extending/extending.rst:995 msgid "" @@ -1661,6 +2005,10 @@ msgid "" "code a borrowed reference can be used after the owner from which it was " "borrowed has in fact disposed of it." msgstr "" +"借用參照相比擁有參照的優點是,你不需要在程式碼中所有可能的路徑上都處理參照的" +"處置 --- 換句話說,使用借用參照時,你不會在提前退出時有洩漏的風險。借用相比擁" +"有的缺點是,在一些微妙的情況下,看似正確的程式碼中的借用參照可能在借出它的擁" +"有者實際上已經處置了它之後被使用。" #: ../../extending/extending.rst:1003 msgid "" @@ -1670,10 +2018,13 @@ msgid "" "full owner responsibilities (the new owner must dispose of the reference " "properly, as well as the previous owner)." msgstr "" +"借用參照可以透過呼叫 :c:func:`Py_INCREF` 變成擁有的參照。這不會影響借出參照的" +"擁有者的狀態 --- 它建立了一個新的擁有參照,並賦予完整的擁有者責任(新的擁有者" +"必須適當地處置參照,就像先前的擁有者一樣)。" #: ../../extending/extending.rst:1013 msgid "Ownership Rules" -msgstr "" +msgstr "擁有權規則" #: ../../extending/extending.rst:1015 msgid "" @@ -1681,6 +2032,8 @@ msgid "" "of the function's interface specification whether ownership is transferred " "with the reference or not." msgstr "" +"每當一個物件參照被傳入或傳出一個函式時,擁有權是否隨參照一起轉移是該函式介面" +"規格的一部分。" #: ../../extending/extending.rst:1019 msgid "" @@ -1692,6 +2045,11 @@ msgid "" "func:`PyLong_FromLong` maintains a cache of popular values and can return a " "reference to a cached item." msgstr "" +"大多數回傳物件參照的函式會連同參照一起傳遞擁有權。特別是,所有用於建立新物件" +"的函式,例如 :c:func:`PyLong_FromLong` 和 :c:func:`Py_BuildValue`,都會將擁有" +"權傳遞給接收者。即使物件實際上不是新的,你仍然會收到該物件的新參照的擁有權。" +"例如,:c:func:`PyLong_FromLong` 維護了一個常用值的快取,並可以回傳指向已快取" +"項目的參照。" #: ../../extending/extending.rst:1027 msgid "" @@ -1703,6 +2061,11 @@ msgid "" "`PyDict_GetItemString` all return references that you borrow from the tuple, " "list or dictionary." msgstr "" +"許多從其他物件中提取物件的函式也會隨著參照轉移擁有權,例如 :c:func:" +"`PyObject_GetAttrString`。然而,這裡的情況不太明確,因為有一些常見的例行程式" +"是例外::c:func:`PyTuple_GetItem`、:c:func:`PyList_GetItem`、:c:func:" +"`PyDict_GetItem` 和 :c:func:`PyDict_GetItemString` 都回傳你從 tuple、list 或 " +"dictionary 中借用的參照。" #: ../../extending/extending.rst:1034 msgid "" @@ -1710,6 +2073,8 @@ msgid "" "even though it may actually create the object it returns: this is possible " "because an owned reference to the object is stored in ``sys.modules``." msgstr "" +"函式 :c:func:`PyImport_AddModule` 也會回傳一個借用參照,即使它可能實際上建立" +"了它回傳的物件:這是可能的,因為該物件的擁有參照被儲存在 ``sys.modules`` 中。" #: ../../extending/extending.rst:1038 msgid "" @@ -1721,6 +2086,11 @@ msgid "" "them --- even if they fail! (Note that :c:func:`PyDict_SetItem` and friends " "don't take over ownership --- they are \"normal.\")" msgstr "" +"當你將一個物件參照傳入另一個函式時,一般來說,該函式會向你借用參照 --- 如果它" +"需要儲存它,它會使用 :c:func:`Py_INCREF` 來成為獨立的擁有者。這個規則恰好有兩" +"個重要的例外::c:func:`PyTuple_SetItem` 和 :c:func:`PyList_SetItem`。這些函式" +"會接管傳入項目的擁有權 --- 即使它們失敗了也是!(注意 :c:func:" +"`PyDict_SetItem` 和其相關函式不會接管擁有權 --- 它們是「正常的」。)" #: ../../extending/extending.rst:1046 msgid "" @@ -1730,6 +2100,9 @@ msgid "" "Only when such a borrowed reference must be stored or passed on, it must be " "turned into an owned reference by calling :c:func:`Py_INCREF`." msgstr "" +"當一個 C 函式被 Python 呼叫時,它會從呼叫者借用其引數的參照。呼叫者擁有該物件" +"的參照,所以借用參照的生命週期在函式回傳之前都是有保證的。只有當這種借用參照" +"必須被儲存或傳遞時,才必須透過呼叫 :c:func:`Py_INCREF` 將它轉換為擁有的參照。" #: ../../extending/extending.rst:1052 msgid "" @@ -1737,10 +2110,12 @@ msgid "" "must be an owned reference --- ownership is transferred from the function to " "its caller." msgstr "" +"從被 Python 呼叫的 C 函式回傳的物件參照必須是擁有的參照 --- 擁有權從函式轉移" +"給它的呼叫者。" #: ../../extending/extending.rst:1060 msgid "Thin Ice" -msgstr "" +msgstr "薄冰" #: ../../extending/extending.rst:1062 msgid "" @@ -1749,6 +2124,8 @@ msgid "" "invocations of the interpreter, which can cause the owner of a reference to " "dispose of it." msgstr "" +"有一些情況下,看似無害的借用參照使用可能會導致問題。這些都與直譯器的隱式叫用" +"有關,它可能導致參照的擁有者處置掉該參照。" #: ../../extending/extending.rst:1066 msgid "" @@ -1756,6 +2133,8 @@ msgid "" "on an unrelated object while borrowing a reference to a list item. For " "instance::" msgstr "" +"需要知道的第一個也是最重要的情況是,在借用 list 項目的參照時,對一個不相關的" +"物件使用 :c:func:`Py_DECREF`。例如: ::" #: ../../extending/extending.rst:1069 msgid "" @@ -1783,6 +2162,8 @@ msgid "" "``list[1]`` with the value ``0``, and finally prints the borrowed reference. " "Looks harmless, right? But it's not!" msgstr "" +"這個函式首先借用了 ``list[0]`` 的參照,然後將 ``list[1]`` 替換為值 ``0``,最" +"後印出借用的參照。看起來無害,對吧?但事實並非如此!" #: ../../extending/extending.rst:1082 msgid "" @@ -1800,6 +2181,15 @@ msgid "" "`442`). This entire sequence happens synchronously within the :c:func:" "`PyList_SetItem` call." msgstr "" +"讓我們追蹤進入 :c:func:`PyList_SetItem` 的控制流程。list 擁有其所有項目的參" +"照,所以當項目 1 被替換時,它必須處置原來的項目 1。現在假設原來的項目 1 是一" +"個使用者定義類別的實例,並且進一步假設該類別定義了一個 :meth:`!__del__` 方" +"法。如果這個類別實例的參照計數為 1,處置它將會呼叫其 :meth:`!__del__` 方法。" +"在內部,:c:func:`PyList_SetItem` 會對被替換的項目呼叫 :c:func:`Py_DECREF`,這" +"會叫用被替換項目對應的 :c:member:`~PyTypeObject.tp_dealloc` 函式。在解除配置" +"期間,:c:member:`~PyTypeObject.tp_dealloc` 會呼叫 :c:member:`~PyTypeObject." +"tp_finalize`,它對於類別實例被對映到 :meth:`!__del__` 方法(見 :pep:`442`)。" +"整個序列在 :c:func:`PyList_SetItem` 呼叫中同步發生。" #: ../../extending/extending.rst:1096 msgid "" @@ -1811,12 +2201,19 @@ msgid "" "this was the last reference to that object, it would free the memory " "associated with it, thereby invalidating ``item``." msgstr "" +"由於它是用 Python 撰寫的,:meth:`!__del__` 方法可以執行任意的 Python 程式碼。" +"它是否有可能做一些事情來使 :c:func:`!bug` 中對 ``item`` 的參照失效?當然可" +"以!假設傳入 :c:func:`!bug` 的 list 對 :meth:`!__del__` 方法是可存取的,它可" +"以執行一個效果等同於 ``del list[0]`` 的陳述式,並且假設這是該物件的最後一個參" +"照,它會釋放與其關聯的記憶體,從而使 ``item`` 失效。" #: ../../extending/extending.rst:1104 msgid "" "The solution, once you know the source of the problem, is easy: temporarily " "increment the reference count. The correct version of the function reads::" msgstr "" +"一旦你知道問題的根源,解決方案很簡單:暫時遞增參照計數。該函式的正確版本如" +"下: ::" #: ../../extending/extending.rst:1107 msgid "" @@ -1848,6 +2245,8 @@ msgid "" "bug and someone spent a considerable amount of time in a C debugger to " "figure out why his :meth:`!__del__` methods would fail..." msgstr "" +"這是一個真實的故事。舊版本的 Python 包含了這個 bug 的變體,有人花了大量時間" +"在 C 除錯器中試圖找出為什麼他的 :meth:`!__del__` 方法會失敗......" #: ../../extending/extending.rst:1122 msgid "" @@ -1861,6 +2260,12 @@ msgid "" "other threads use the processor while waiting for the I/O to complete. " "Obviously, the following function has the same problem as the previous one::" msgstr "" +"借用參照問題的第二種情況是涉及執行緒的變體。通常,Python 直譯器中的多個執行緒" +"不會互相干擾,因為有一個\\ :term:`全域鎖定 `\\ 保護" +"著 Python 的整個物件空間。然而,可以使用巨集 :c:macro:" +"`Py_BEGIN_ALLOW_THREADS` 暫時釋放此鎖定,並使用 :c:macro:" +"`Py_END_ALLOW_THREADS` 重新取得它。這在阻塞式 I/O 呼叫周圍很常見,讓其他執行" +"緒可以在等待 I/O 完成時使用處理器。顯然,以下函式與前一個有相同的問題: ::" #: ../../extending/extending.rst:1132 msgid "" @@ -1874,6 +2279,15 @@ msgid "" " PyObject_Print(item, stdout, 0); /* BUG! */\n" "}" msgstr "" +"void\n" +"bug(PyObject *list)\n" +"{\n" +" PyObject *item = PyList_GetItem(list, 0);\n" +" Py_BEGIN_ALLOW_THREADS\n" +" ...some blocking I/O call...\n" +" Py_END_ALLOW_THREADS\n" +" PyObject_Print(item, stdout, 0); /* BUG! */\n" +"}" #: ../../extending/extending.rst:1146 msgid "NULL Pointers" @@ -1890,6 +2304,11 @@ msgid "" "``NULL``, there would be a lot of redundant tests and the code would run " "more slowly." msgstr "" +"一般而言,接受物件參照作為引數的函式不會預期你傳入 ``NULL`` 指標,如果你這樣" +"做會導致核心傾印(core dump)(或導致之後的核心傾印)。回傳物件參照的函式通常" +"只在有例外發生時才回傳 ``NULL``。不對 ``NULL`` 引數進行測試的原因是,函式經常" +"將它們收到的物件傳遞給其他函式──如果每個函式都測試 ``NULL``,就會有大量多餘的" +"測試,而且程式碼會執行得更慢。" #: ../../extending/extending.rst:1156 msgid "" @@ -1897,6 +2316,8 @@ msgid "" "that may be ``NULL`` is received, for example, from :c:func:`malloc` or from " "a function that may raise an exception." msgstr "" +"比較好的做法是只在「源頭」測試 ``NULL``:當收到可能為 ``NULL`` 的指標時,例如" +"從 :c:func:`malloc` 或從可能引發例外的函式收到時。" #: ../../extending/extending.rst:1160 msgid "" @@ -1904,6 +2325,8 @@ msgid "" "``NULL`` pointers --- however, their variants :c:func:`Py_XINCREF` and :c:" "func:`Py_XDECREF` do." msgstr "" +"巨集 :c:func:`Py_INCREF` 和 :c:func:`Py_DECREF` 不會檢查 ``NULL`` 指標──然" +"而,它們的變體 :c:func:`Py_XINCREF` 和 :c:func:`Py_XDECREF` 會。" #: ../../extending/extending.rst:1164 msgid "" @@ -1913,6 +2336,9 @@ msgid "" "expected types, and this would generate redundant tests. There are no " "variants with ``NULL`` checking." msgstr "" +"用來檢查特定物件型別的巨集(``Pytype_Check()``)不會檢查 ``NULL`` 指標──同樣" +"地,有很多程式碼會連續呼叫數個這類巨集來測試一個物件是否符合各種不同的預期型" +"別,這樣會產生多餘的測試。沒有帶 ``NULL`` 檢查的變體。" #: ../../extending/extending.rst:1170 msgid "" @@ -1920,16 +2346,18 @@ msgid "" "C functions (``args`` in the examples) is never ``NULL`` --- in fact it " "guarantees that it is always a tuple [#]_." msgstr "" +"C 函式呼叫機制保證傳遞給 C 函式的引數列表(範例中的 ``args``)永遠不會是 " +"``NULL``\\ ──事實上它保證它始終是一個 tuple [#]_。" #: ../../extending/extending.rst:1174 msgid "" "It is a severe error to ever let a ``NULL`` pointer \"escape\" to the Python " "user." -msgstr "" +msgstr "讓一個 ``NULL`` 指標「逃逸」到 Python 使用者端是一個嚴重的錯誤。" #: ../../extending/extending.rst:1185 msgid "Writing Extensions in C++" -msgstr "" +msgstr "以 C++ 撰寫擴充" #: ../../extending/extending.rst:1187 msgid "" @@ -1943,10 +2371,16 @@ msgid "" "`` --- they use this form already if the symbol ``__cplusplus`` is defined " "(all recent C++ compilers define this symbol)." msgstr "" +"可以用 C++ 撰寫擴充模組。有一些限制。如果主程式(Python 直譯器)是由 C 編譯器" +"編譯和連結的,則不能使用帶有建構函式 (constructor) 的全域或靜態物件。如果主程" +"式是由 C++ 編譯器連結的,則沒有這個問題。會被 Python 直譯器呼叫的函式(特別是" +"模組初始化函式)必須使用 ``extern \"C\"`` 來宣告。不需要將 Python 標頭檔包在 " +"``extern \"C\" {...}`` 中──如果定義了符號 ``__cplusplus``,它們已經使用了這個" +"形式(所有近期的 C++ 編譯器都定義了這個符號)。" #: ../../extending/extending.rst:1201 msgid "Providing a C API for an Extension Module" -msgstr "" +msgstr "為擴充模組提供 C API" #: ../../extending/extending.rst:1206 msgid "" @@ -1958,6 +2392,11 @@ msgid "" "create and manipulate lists, this new collection type should have a set of C " "functions for direct manipulation from other extension modules." msgstr "" +"許多擴充模組只是提供新的函式和型別供 Python 使用,但有時擴充模組中的程式碼也" +"可能對其他擴充模組有用。例如,一個擴充模組可以實作一種「collection」型別,它" +"的運作方式類似無序的 list。就像標準的 Python list 型別有一個 C API 允許擴充模" +"組建立和操作 list 一樣,這個新的 collection 型別也應該有一組 C 函式供其他擴充" +"模組直接操作。" #: ../../extending/extending.rst:1214 msgid "" @@ -1974,6 +2413,14 @@ msgid "" "if symbols are globally visible, the module whose functions one wishes to " "call might not have been loaded yet!" msgstr "" +"乍看之下這似乎很容易:只要撰寫函式(當然不要宣告為 ``static``),提供適當的標" +"頭檔,並撰寫 C API 的文件。事實上,如果所有擴充模組都是靜態連結到 Python 直譯" +"器的,這樣做是行得通的。然而,當模組作為共享函式庫使用時,在一個模組中定義的" +"符號可能對另一個模組不可見。可見性的細節取決於作業系統;有些系統對 Python 直" +"譯器和所有擴充模組使用同一個全域命名空間(例如 Windows),而其他系統則要求在" +"模組連結時明確列出匯入的符號(AIX 就是一個例子),或者提供不同策略的選擇(大" +"多數 Unix 系統)。即使符號是全域可見的,想要呼叫其函式的模組也可能尚未被載" +"入!" #: ../../extending/extending.rst:1226 msgid "" @@ -1984,6 +2431,10 @@ msgid "" "section :ref:`methodtable`). And it means that symbols that *should* be " "accessible from other extension modules must be exported in a different way." msgstr "" +"因此,可攜性要求不對符號可見性做任何假設。這意味著擴充模組中的所有符號都應該" +"宣告為 ``static``,模組的初始化函式除外,以避免與其他擴充模組的名稱衝突(如在" +"\\ :ref:`methodtable`\\ 章節中所討論的)。而且這意味著\\ *應該*\\ 從其他擴充" +"模組存取的符號必須以不同的方式匯出。" #: ../../extending/extending.rst:1233 msgid "" @@ -1996,6 +2447,11 @@ msgid "" "module, retrieve the value of this name, and then retrieve the pointer from " "the Capsule." msgstr "" +"Python 提供了一種特殊機制,用來在不同擴充模組之間傳遞 C 層級的資訊(指標):" +"Capsule。Capsule 是一種 Python 資料型別,它儲存一個指標(:c:expr:`void " +"\\*`)。Capsule 只能透過其 C API 來建立和存取,但它們可以像任何其他 Python 物" +"件一樣被傳遞。特別是,它們可以被指派給擴充模組命名空間中的一個名稱。其他擴充" +"模組可以接著 import 這個模組、取得這個名稱的值,然後從 Capsule 中取得指標。" #: ../../extending/extending.rst:1241 msgid "" @@ -2006,6 +2462,10 @@ msgid "" "distributed in different ways between the module providing the code and the " "client modules." msgstr "" +"有許多方式可以使用 Capsule 來匯出擴充模組的 C API。每個函式可以擁有自己的 " +"Capsule,或者所有 C API 指標可以儲存在一個陣列中,其位址發布在一個 Capsule " +"裡。儲存和取得指標的各種任務可以在提供程式碼的模組和用戶端模組之間以不同方式" +"分配。" #: ../../extending/extending.rst:1247 msgid "" @@ -2016,12 +2476,16 @@ msgid "" "of runtime type-safety; there is no feasible way to tell one unnamed Capsule " "from another." msgstr "" +"無論你選擇哪種方式,正確命名你的 Capsule 都很重要。函式 :c:func:" +"`PyCapsule_New` 接受一個名稱參數(:c:expr:`const char \\*`);你可以傳入 " +"``NULL`` 名稱,但我們強烈建議你指定一個名稱。正確命名的 Capsule 提供了一定程" +"度的執行期型別安全性;沒有可行的方法來區分一個未命名的 Capsule 和另一個。" #: ../../extending/extending.rst:1254 msgid "" "In particular, Capsules used to expose C APIs should be given a name " "following this convention::" -msgstr "" +msgstr "特別是,用於公開 C API 的 Capsule 應該依照此慣例來命名: ::" #: ../../extending/extending.rst:1257 msgid "modulename.attributename" @@ -2034,6 +2498,9 @@ msgid "" "convention. This behavior gives C API users a high degree of certainty that " "the Capsule they load contains the correct C API." msgstr "" +"便利函式 :c:func:`PyCapsule_Import` 使得載入透過 Capsule 提供的 C API 變得容" +"易,但前提是 Capsule 的名稱符合此慣例。這個行為讓 C API 使用者對於他們載入的 " +"Capsule 包含正確的 C API 有高度的確定性。" #: ../../extending/extending.rst:1264 msgid "" @@ -2045,6 +2512,11 @@ msgid "" "takes care of importing the module and retrieving its C API pointers; client " "modules only have to call this macro before accessing the C API." msgstr "" +"以下範例展示了一種將大部分負擔放在匯出模組的撰寫者身上的方法,這對於常用的函" +"式庫模組來說是適當的。它將所有 C API 指標(在範例中只有一個!)儲存在一個 :c:" +"expr:`void` 指標陣列中,該陣列成為 Capsule 的值。與該模組對應的標頭檔提供了一" +"個巨集,負責 import 模組並取得其 C API 指標;用戶端模組只需在存取 C API 之前" +"呼叫這個巨集即可。" #: ../../extending/extending.rst:1272 msgid "" @@ -2056,12 +2528,19 @@ msgid "" "function :c:func:`!PySpam_System` is also exported to other extension " "modules." msgstr "" +"匯出模組是\\ :ref:`extending-simpleexample`\\ 章節中 :mod:`!spam` 模組的修改" +"版。函式 :func:`!spam.system` 不直接呼叫 C 函式庫函式 :c:func:`system`,而是" +"呼叫函式 :c:func:`!PySpam_System`,這個函式在現實中當然會做更複雜的事情(例如" +"在每個命令中加上「spam」)。這個函式 :c:func:`!PySpam_System` 也會匯出給其他" +"擴充模組。" #: ../../extending/extending.rst:1279 msgid "" "The function :c:func:`!PySpam_System` is a plain C function, declared " "``static`` like everything else::" msgstr "" +"函式 :c:func:`!PySpam_System` 是一個普通的 C 函式,和其他所有東西一樣宣告為 " +"``static``: ::" #: ../../extending/extending.rst:1282 msgid "" @@ -2079,7 +2558,7 @@ msgstr "" #: ../../extending/extending.rst:1288 msgid "The function :c:func:`!spam_system` is modified in a trivial way::" -msgstr "" +msgstr "函式 :c:func:`!spam_system` 做了簡單的修改: ::" #: ../../extending/extending.rst:1290 msgid "" @@ -2109,7 +2588,7 @@ msgstr "" #: ../../extending/extending.rst:1302 msgid "In the beginning of the module, right after the line ::" -msgstr "" +msgstr "在模組的開頭,緊接在這一行之後: ::" #: ../../extending/extending.rst:1304 msgid "#include " @@ -2117,7 +2596,7 @@ msgstr "#include " #: ../../extending/extending.rst:1306 msgid "two more lines must be added::" -msgstr "" +msgstr "必須再加上兩行: ::" #: ../../extending/extending.rst:1308 msgid "" @@ -2134,6 +2613,8 @@ msgid "" "`mod_exec ` function must take care of initializing the C API " "pointer array::" msgstr "" +"``#define`` 用來告訴標頭檔它是被引入到匯出模組中,而不是用戶端模組。最後,模" +"組的 :c:data:`mod_exec ` 函式必須負責初始化 C API 指標陣列: ::" #: ../../extending/extending.rst:1315 msgid "" @@ -2157,18 +2638,39 @@ msgid "" " return 0;\n" "}" msgstr "" +"static int\n" +"spam_module_exec(PyObject *m)\n" +"{\n" +" static void *PySpam_API[PySpam_API_pointers];\n" +" PyObject *c_api_object;\n" +"\n" +" /* 初始化 C API 指標陣列 */\n" +" PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;\n" +"\n" +" /* 建立一個包含 API 指標陣列位址的 Capsule */\n" +" c_api_object = PyCapsule_New((void *)PySpam_API, \"spam._C_API\", " +"NULL);\n" +"\n" +" if (PyModule_Add(m, \"_C_API\", c_api_object) < 0) {\n" +" return -1;\n" +" }\n" +"\n" +" return 0;\n" +"}" #: ../../extending/extending.rst:1334 msgid "" "Note that ``PySpam_API`` is declared ``static``; otherwise the pointer array " "would disappear when :c:func:`!PyInit_spam` terminates!" msgstr "" +"請注意 ``PySpam_API`` 被宣告為 ``static``;否則指標陣列會在 :c:func:`!" +"PyInit_spam` 結束時消失!" #: ../../extending/extending.rst:1337 msgid "" "The bulk of the work is in the header file :file:`spammodule.h`, which looks " "like this::" -msgstr "" +msgstr "大部分的工作在標頭檔 :file:`spammodule.h` 中,它看起來像這樣: ::" #: ../../extending/extending.rst:1340 msgid "" @@ -2221,6 +2723,54 @@ msgid "" "\n" "#endif /* !defined(Py_SPAMMODULE_H) */" msgstr "" +"#ifndef Py_SPAMMODULE_H\n" +"#define Py_SPAMMODULE_H\n" +"#ifdef __cplusplus\n" +"extern \"C\" {\n" +"#endif\n" +"\n" +"/* spammodule 的標頭檔 */\n" +"\n" +"/* C API 函式 */\n" +"#define PySpam_System_NUM 0\n" +"#define PySpam_System_RETURN int\n" +"#define PySpam_System_PROTO (const char *command)\n" +"\n" +"/* C API 指標的總數 */\n" +"#define PySpam_API_pointers 1\n" +"\n" +"\n" +"#ifdef SPAM_MODULE\n" +"/* 此區段在編譯 spammodule.c 時使用 */\n" +"\n" +"static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;\n" +"\n" +"#else\n" +"/* 此區段在使用 spammodule API 的模組中使用 */\n" +"\n" +"static void **PySpam_API;\n" +"\n" +"#define PySpam_System \\\n" +" (*(PySpam_System_RETURN (*)PySpam_System_PROTO) " +"PySpam_API[PySpam_System_NUM])\n" +"\n" +"/* 錯誤時回傳 -1,成功時回傳 0。\n" +" * PyCapsule_Import 會在發生錯誤時設定例外。\n" +" */\n" +"static int\n" +"import_spam(void)\n" +"{\n" +" PySpam_API = (void **)PyCapsule_Import(\"spam._C_API\", 0);\n" +" return (PySpam_API != NULL) ? 0 : -1;\n" +"}\n" +"\n" +"#endif\n" +"\n" +"#ifdef __cplusplus\n" +"}\n" +"#endif\n" +"\n" +"#endif /* !defined(Py_SPAMMODULE_H) */" #: ../../extending/extending.rst:1388 msgid "" @@ -2228,6 +2778,9 @@ msgid "" "func:`!PySpam_System` is to call the function (or rather macro) :c:func:`!" "import_spam` in its :c:data:`mod_exec ` function::" msgstr "" +"用戶端模組要存取函式 :c:func:`!PySpam_System`,所要做的就是在其 :c:data:" +"`mod_exec ` 函式中呼叫函式(或者更確切地說是巨集)\\ :c:func:`!" +"import_spam`: ::" #: ../../extending/extending.rst:1392 msgid "" @@ -2241,6 +2794,15 @@ msgid "" " return 0;\n" "}" msgstr "" +"static int\n" +"client_module_exec(PyObject *m)\n" +"{\n" +" if (import_spam() < 0) {\n" +" return -1;\n" +" }\n" +" /* 額外的初始化可以在這裡進行 */\n" +" return 0;\n" +"}" #: ../../extending/extending.rst:1402 msgid "" @@ -2248,6 +2810,8 @@ msgid "" "is rather complicated. However, the basic structure is the same for each " "function that is exported, so it has to be learned only once." msgstr "" +"這種方法的主要缺點是檔案 :file:`spammodule.h` 相當複雜。然而,每個被匯出的函" +"式的基本結構都是相同的,所以只需要學習一次。" #: ../../extending/extending.rst:1406 msgid "" @@ -2258,6 +2822,10 @@ msgid "" "Capsules (files :file:`Include/pycapsule.h` and :file:`Objects/pycapsule.c` " "in the Python source code distribution)." msgstr "" +"最後應該提到的是,Capsule 提供了額外的功能,這對於 Capsule 中儲存的指標的記憶" +"體配置和釋放特別有用。詳細資訊在 Python/C API 參考手冊的 :ref:`capsules` 章節" +"以及 Capsule 的實作中有描述(Python 原始碼發行版中的 :file:`Include/" +"pycapsule.h` 和 :file:`Objects/pycapsule.c` 檔案)。" #: ../../extending/extending.rst:1414 msgid "Footnotes" @@ -2268,12 +2836,14 @@ msgid "" "An interface for this function already exists in the standard module :mod:" "`os` --- it was chosen as a simple and straightforward example." msgstr "" +"這個函式的介面已存在於標準模組 :mod:`os` 中──它被選用是因為它是一個簡單且直觀" +"的範例。" #: ../../extending/extending.rst:1418 msgid "" "The metaphor of \"borrowing\" a reference is not completely correct: the " "owner still has a copy of the reference." -msgstr "" +msgstr "「借用」參照的比喻並不完全正確:擁有者仍然持有參照的副本。" #: ../../extending/extending.rst:1421 msgid "" @@ -2281,12 +2851,16 @@ msgid "" "reference count itself could be in freed memory and may thus be reused for " "another object!" msgstr "" +"檢查參照計數是否至少為 1 是\\ **行不通的**\\ ──參照計數本身可能位於已釋放的記" +"憶體中,因此可能被另一個物件重新使用!" #: ../../extending/extending.rst:1425 msgid "" "These guarantees don't hold when you use the \"old\" style calling " "convention --- this is still found in much existing code." msgstr "" +"當你使用「舊」式的呼叫慣例時,這些保證不成立──這在許多現有的程式碼中仍然可以" +"找到。" #: ../../extending/extending.rst:593 msgid "PyObject_CallObject (C function)"