# 擴充/嵌入常見問題集

## 我可以在 C 中建立自己的函式嗎？

是的，你可以在 C 中建立包含函式、變數、例外甚至新型別的內建模組，擴充和嵌入 Python 直譯器 文件中有相關說明。

大多數中級或進階 Python 書籍也會涵蓋這個主題。

## 我可以在 C++ 中建立自己的函式嗎？

是的，可使用 C++ 中的 C 相容性功能。將 `extern "C" { ... }` 放在 Python 引入檔案周圍，並將 `extern "C"` 放在每個將由 Python 直譯器呼叫的函式之前。但具有構造函式的全域或靜態 C++ 物件可能不是一個好主意。

## 寫 C 很難；還有其他選擇嗎？

There are a number of alternatives to writing your own C extensions, depending on what you're trying to do. Recommended third party tools offer both simpler and more sophisticated approaches to creating C and C++ extensions for Python.

## 如何從 C 執行任意 Python 陳述式？

The highest-level function to do this is `PyRun_SimpleString()` which takes a single string argument to be executed in the context of the module `__main__` and returns `0` for success and `-1` when an exception occurred (including `SyntaxError`). If you want more control, use `PyRun_String()`; see the source for `PyRun_SimpleString()` in `Python/pythonrun.c`.

## How can I evaluate an arbitrary Python expression from C?

Call the function `PyRun_String()` from the previous question with the start symbol `Py_eval_input`; it parses an expression, evaluates it and returns its value.

## 如何從 Python 物件中提取 C 值？

That depends on the object's type. If it's a tuple, `PyTuple_Size()` returns its length and `PyTuple_GetItem()` returns the item at a specified index. Lists have similar functions, `PyList_Size()` and `PyList_GetItem()`.

For bytes, `PyBytes_Size()` returns its length and `PyBytes_AsStringAndSize()` provides a pointer to its value and its length. Note that Python bytes objects may contain null bytes so C's `strlen()` should not be used.

要測試物件的型別，首先確保它不是 `NULL`，然後再使用 `PyBytes_Check()`、`PyTuple_Check()`、`PyList_Check()` 等函式。

There is also a high-level API to Python objects which is provided by the so-called 'abstract' interface -- read `Include/abstract.h` for further details. It allows interfacing with any kind of Python sequence using calls like `PySequence_Length()`, `PySequence_GetItem()`, etc. as well as many other useful protocols such as numbers (`PyNumber_Index()` et al.) and mappings in the PyMapping APIs.

## 如何使用 Py_BuildValue() 建立任意長度的元組？

這無法做到。請改用 `PyTuple_Pack()`。

## 如何從 C 呼叫物件的方法？

The `PyObject_CallMethod()` function can be used to call an arbitrary method of an object. The parameters are the object, the name of the method to call, a format string like that used with `Py_BuildValue()`, and the argument values:

    PyObject *
    PyObject_CallMethod(PyObject *object, const char *method_name,
                        const char *arg_format, ...);

這適用於任何具有方法的物件 —— 無論是內建的還是使用者定義的。你負責最終為回傳值來 `Py_DECREF()`。

例如，使用引數 10、0 呼叫檔案物件的 "seek" 方法（假設檔案物件指標為 "f"）：

    res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
    if (res == NULL) {
            ... 發生一個例外 ...
    }
    else {
            Py_DECREF(res);
    }

請注意，由於 `PyObject_CallObject()` 總是需要一個元組作為引數列表，若要呼叫一個不帶引數的函式，要傳遞 "()" 作為格式，並呼叫一個帶有一個引數的函式，將引數括起來在括號中，例如 "(i)"。

## 我如何捕捉 PyErr_Print() 的輸出（或任何印出到 stdout/stderr 的東西）？

In Python code, define an object that supports the `write()` method. Assign this object to `sys.stdout` and `sys.stderr`. Call print_error, or just allow the standard traceback mechanism to work. Then, the output will go wherever your `write()` method sends it.

最簡單的方法是使用 `io.StringIO` 類別：

    >>> import io, sys
    >>> sys.stdout = io.StringIO()
    >>> print('foo')
    >>> print('hello world!')
    >>> sys.stderr.write(sys.stdout.getvalue())
    foo
    hello world!

執行相同操作的自定義物件如下所示：

    >>> import io, sys
    >>> class StdoutCatcher(io.TextIOBase):
    ...     def __init__(self):
    ...         self.data = []
    ...     def write(self, stuff):
    ...         self.data.append(stuff)
    ...
    >>> import sys
    >>> sys.stdout = StdoutCatcher()
    >>> print('foo')
    >>> print('hello world!')
    >>> sys.stderr.write(''.join(sys.stdout.data))
    foo
    hello world!

## 如何從 C 存取用 Python 編寫的模組？

你可以取得指向模組物件的指標，如下所示：

    module = PyImport_ImportModule("<modulename>");

如果模組還沒有被引入（即它還沒有出現在 `sys.modules` 中），這會初始化模組；否則它只回傳 `sys.modules["<modulename>"]` 的值。請注意，它不會將模組輸入任何命名空間——它只會確保它已被初始化並儲存在 `sys.modules` 中。

然後你可以存取模組的屬性（即模組中定義的任何名稱），如下所示：

    attr = PyObject_GetAttrString(module, "<attrname>");

Calling `PyObject_SetAttrString()` to assign to variables in the module also works.

## 我如何從 Python 介接到 C++ 物件？

根據你的要求不同而有多種不同方法。要手動執行此操作，請先閱讀「擴充和嵌入」說明文件。對於 Python run-time 系統，C 和 C++ 之間並沒有太多區別 —— 因此圍繞 C 結構（指標）型別來構建新 Python 型別的策略也適用於 C++ 物件。

對於 C++ 函式庫，請參閱 寫 C 很難；還有其他選擇嗎？。

## 我使用安裝檔案新增了一個模組，但 make 失敗了；為什麼？

Setup must end in a newline, if there is no newline there, the build process fails. (Fixing this requires some ugly shell script hackery, and this bug is so minor that it doesn't seem worth the effort.)

## 如何為擴充套件除錯？

When using GDB with dynamically loaded extensions, you can't set a breakpoint in your extension until your extension is loaded.

在你的 `.gdbinit` 檔案中（或交互地），新增命令：

    br _PyImport_LoadDynamicModule

然後，當你運行 GDB 時：

    $ gdb /local/bin/python
    gdb) run myscript.py
    gdb) continue # repeat until your extension is loaded
    gdb) finish   # so that your extension is loaded
    gdb) br myfunction.c:50
    gdb) continue

## 我想在我的 Linux 系統上編譯一個 Python 模組，但是缺少一些檔案。為什麼？

Most packaged versions of Python omit some files required for compiling Python extensions.

在 Red Hat 上，請安裝 python3-devel RPM 來取得必要的檔案。

對於 Debian，運行 `apt-get install python3-dev`。

## 如何從「無效輸入」區分出「不完整輸入」？

Sometimes you want to emulate the Python interactive interpreter's behavior, where it gives you a continuation prompt when the input is incomplete (e.g. you typed the start of an "if" statement or you didn't close your parentheses or triple string quotes), but it gives you a syntax error message immediately when the input is invalid.

在 Python 中，你可以使用 `codeop` 模組，它充分模擬了剖析器 (parser) 的行為。像是 IDLE 就有使用它。

The easiest way to do it in C is to call `PyRun_InteractiveLoop()` (perhaps in a separate thread) and let the Python interpreter handle the input for you. You can also set the `PyOS_ReadlineFunctionPointer()` to point at your custom input function. See `Modules/readline.c` and `Parser/myreadline.c` for more hints.

## 如何找到未定義的 g++ 符號 __builtin_new 或 __pure_virtual？

To dynamically load g++ extension modules, you must recompile Python, relink it using g++ (change LINKCC in the Python Modules Makefile), and link your extension module using g++ (e.g., `g++ -shared -o mymodule.so mymodule.o`).

## 我可以用一些用 C 實作的方法和用 Python 實作的其他方法（例如透過繼承）建立一個物件類別嗎？

是的，你可以繼承內建類別，例如 `int`、`list`、`dict` 等。

Boost Python 函式庫（BPL，https://www.boost.org/libs/python/doc/index.html）提供了一種從 C++ 執行此操作的方法（即你可以使用 BPL 來繼承用 C++ 編寫的擴充類別）。