最終更新:2026-10-12
pybind11 で Python を高速化¶
通常、時間のかかる処理は C++ で実装し、Python から対応するインターフェースを呼び出したいことがあります。あるいは基盤ライブラリの事情で外部向けの Python API をサポートする必要がある場合、Python から C++ にアクセスできるようにする必要があります。どう実現すればよいのでしょうか?
方法はいろいろありますが、この節では pybind11 を例に、完全なプロジェクトテンプレートを導入します。今後このようなニーズが生じた場合は、テンプレートをベースに改変できます。
注:(楽をしたい方向け)この節のすべてのコードも星球に置いてあります。
1.マクロ展開¶
仮定として、比較的時間のかかるソートを例にします。C++ でソートを書き、それを Python で使いたい場合、どう書けばよいでしょうか?
C++ のプログラムは非常にシンプルで、外部に 1 つのインターフェースを公開し、その中でソートを行えばよいのです。例えば:
std::vector<int> sort_vector(const std::vector<int>& input) {
std::vector<int> result = input;
std::sort(result.begin(), result.end());
return result;
}
そこで問題です。Pybind11 では、このインターフェースをどのように使うのでしょうか?
pybind11 のマクロを使えばモジュールを 1 つ定義できます。例えば:
PYBIND11_MODULE(sort_module, m) {
m.doc() = "pybind11 example plugin"; // Optional module docstring
m.def("sort_vector", &sort_vector, "A function that sorts a list of integers");
}
マクロ展開の結果は:PyInit_sort_module です。
python で xxx ライブラリを import するとき、Python インタプリタは C/C++ で書かれた拡張モジュールをロードする際に自動的に呼び出される入口関数があり、その入口関数が次の PyInit_sort_module です:
static ::pybind11::module_::module_def pybind11_module_def_sort_module [[maybe_unused]];
[[maybe_unused]] static void pybind11_init_sort_module(::pybind11::module_ &);
extern "C" [[maybe_unused]] __attribute__((visibility("default"))) PyObject *PyInit_sort_module();
extern "C" __attribute__((visibility("default"))) PyObject *PyInit_sort_module() {
{
const char *compiled_ver = "3" "." "11";
const char *runtime_ver = Py_GetVersion();
size_t len = std::strlen(compiled_ver);
if (std::strncmp(runtime_ver, compiled_ver, len) != 0 || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) {
PyErr_Format(PyExc_ImportError,
"Python version mismatch: module was compiled for Python %s, "
"but the interpreter version is incompatible: %s.",
compiled_ver, runtime_ver);
return nullptr;
}
}
pybind11::detail::get_internals();
auto m = ::pybind11::module_::create_extension_module("sort_module", nullptr, &pybind11_module_def_sort_module);
try {
pybind11_init_sort_module(m);
return m.ptr();
} catch (pybind11::error_already_set & e) {
pybind11::raise_from(e, PyExc_ImportError, "initialization failed");
return nullptr;
} catch (const std::exception &e) {
::pybind11::set_error(PyExc_ImportError, e.what());
return nullptr;
}
}
void pybind11_init_sort_module(::pybind11::module_ & m) {
m.doc() = "pybind11 example plugin";
m.def("sort_vector", &sort_vector, "A function that sorts a list of integers");
}
そのため、このマクロを使わないと、ライブラリを import するときにエラーになります:
>>> import sort_module
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: dynamic module does not define module export function (PyInit_sort_module)
2.CMake + pybind11¶
C++ での pybind11 の使用は次のとおりです:
- ヘッダファイル
- マクロ
では、cmake で pybind11 をダウンロードしてリンクするにはどうすればよいでしょうか?
FetchContent モジュールを使うだけです。例えば:
次に CMakeLists に自分のモジュールを追加するだけです:
cmake が pybingd11 のダウンロードとコンパイルまで行ってくれます。
3.使用¶
上の手順で so が 1 つ生成されるので、python の site-packages ディレクトリに install すると、import の際に認識されます。以下は自作モジュールのデモです:
import sort_module
def test_sort():
input_list = [5, 2, 3, 1, 4]
sorted_list = sort_module.sort_vector(input_list)
print(f"Original list: {input_list}")
print(f"Sorted list: {sorted_list}")
if __name__ == "__main__":
test_sort()
実行:
関連トピック¶
- C++ FnOnce を手書きする | C++那些事
- C++ gRPC ミニプロジェクト | C++那些事
- C++ プロセス間通信実践 | C++那些事
- C++ 短縮URLサービス Demo | C++那些事
- C++17 と SQLite でディレクトリサイズを集計 | C++那些事
- Google Benchmark 性能テスト | C++那些事
- C++20 コルーチン HTTP サーバ | C++那些事
- Protobuf ZeroCopy シリアライズ | C++那些事
- C++ スタック型電卓の設計 | C++那些事