结合pybind11官方文档,利用ai整理
pybind11 全面使用指南
Date: October 30, 2025
Code: https://github.com/pybind/pybind11
目录
概述
安装指南
快速开始
函数绑定
类绑定
STL容器支持
高级特性
跨平台构建
最佳实践
常见问题解决
概述
pybind11是一个轻量级的头文件库,用于在C11和Python之间创建无缝的互操作性。它支持将C 函数、类和数据结构暴露给Python,反之亦然。
主要特性
头文件库 : 无需链接额外的库
跨平台 : 支持Windows、Linux、macOS
自动类型转换 : 基本数据类型和STL容器的自动转换
C++11及以上 : 充分利用现代C++特性
Python 3.8+ : 支持最新的Python版本
支持的编译器
平台
编译器支持
Python版本
特殊要求
Windows
MSVC 2022+, Clang, GCC
3.8+
需要Visual Studio Build Tools
Linux
GCC 4.8+, Clang 3.3+
3.8+
开发工具链完备
macOS
Clang 5.0+
3.8+
Xcode命令行工具
安装指南
Windows安装
方法1: 使用pip安装
方法2: 使用conda安装
1 conda install -c conda-forge pybind11
方法3: 源码安装
1 2 3 4 5 6 7 git clone https://github.com/pybind/pybind11.git cd pybind11mkdir buildcd buildcmake .. -G "Visual Studio 17 2022" -A x64 cmake --build . --config Release cmake --install .
Linux安装
方法1: 使用包管理器
1 2 3 4 5 6 sudo apt updatesudo apt install pybind11-devsudo dnf install pybind11-devel
方法2: 使用pip安装
方法3: 源码安装
1 2 3 4 5 6 7 git clone https://github.com/pybind/pybind11.git cd pybind11mkdir buildcd buildcmake .. -DCMAKE_BUILD_TYPE=Release make -j4 sudo make install
验证安装
1 2 3 4 5 python -m pybind11 --version python -m pybind11 --includes
快速开始
第一个示例
创建一个简单的C++函数并暴露给Python。
1. 创建C++文件 (example.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 #include <pybind11/pybind11.h> namespace py = pybind11;int add (int i, int j) { return i + j; } PYBIND11_MODULE (example, m) { m.doc () = "pybind11 example plugin" ; m.def ("add" , &add, "A function that adds two numbers" ); }
2. 编译
Linux/macOS:
1 c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
Windows (Visual Studio):
1 cl /O2 /LD /EHsc /I %PYBIND11_INCLUDE% /I %PYTHON_INCLUDE% example.cpp /link /LIBPATH:%PYTHON_LIB% python314.lib /OUT:example.pyd
3. 在Python中使用
1 2 3 import exampleprint (example.add(1 , 2 )) print (example.__doc__)
函数绑定
基本参数传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <pybind11/pybind11.h> #include <string> namespace py = pybind11;int add (int a, int b) { return a + b; } double multiply (double x, double y) { return x * y; } std::string greet (const std::string& name) { return "Hello, " + name + "!" ; } PYBIND11_MODULE (example, m) { m.def ("add" , &add, "Add two integers" ); m.def ("multiply" , &multiply, "Multiply two doubles" ); m.def ("greet" , &greet, "Greet someone by name" ); }
关键字参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <pybind11/pybind11.h> #include <tuple> namespace py = pybind11;std::tuple<int , int , int > create_point (int x, int y, int z) { return std::make_tuple (x, y, z); } PYBIND11_MODULE (example, m) { m.def ("create_point" , &create_point, py::arg ("x" ), py::arg ("y" ), py::arg ("z" ), "Create a 3D point" ); using namespace pybind11::literals; m.def ("create_point_short" , &create_point, "x" _a, "y" _a, "z" _a, "Create a 3D point (short notation)" ); }
Python使用:
1 point = example.create_point(x=10 , y=20 , z=30 )
默认参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <pybind11/pybind11.h> namespace py = pybind11;int power (int base, int exponent = 2 ) { int result = 1 ; for (int i = 0 ; i < exponent; i++) { result *= base; } return result; } PYBIND11_MODULE (example, m) { m.def ("power" , &power, py::arg ("base" ), py::arg ("exponent" ) = 2 , "Calculate power of a number" ); }
Python使用:
1 2 result1 = example.power(5 ) result2 = example.power(5 , 3 )
禁止类型转换
1 2 3 4 5 6 7 8 9 10 11 12 #include <pybind11/pybind11.h> namespace py = pybind11;void strict_int_function (int value) { } PYBIND11_MODULE (example, m) { m.def ("strict_int_function" , &strict_int_function, py::arg ("value" ).noconvert ()); }
函数重载
1. 静态类型转换(static_cast)
通过static_cast将成员函数指针转换为特定重载版本的类型,明确指定参数列表,从而绑定目标方法。
1 2 3 py::class_ <Pet>(m, "Pet" ) .def ("set" , static_cast <void (Pet::*)(int )>(&Pet::set), "Set the pet's age" ) .def ("set" , static_cast <void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name" );
2. C++14及以上:py::overload_cast(推荐)
对于支持C++14的编译器,可使用py::overload_cast,只需指定参数类型(返回类型和所属类会自动推导),语法更简洁。
1 2 3 py::class_ <Pet>(m, "Pet" ) .def ("set" , py::overload_cast <int >(&Pet::set), "Set the pet's age" ) .def ("set" , py::overload_cast <const std::string &>(&Pet::set), "Set the pet's name" );
3. 区分const重载(const与非const版本)
当方法因const限定符重载时(如void func() 和void func() const),需用py::const_标签区分const版本。
1 2 3 4 5 6 7 8 struct Widget { int foo (int x, float y) ; int foo (int x, float y) const ; }; py::class_ <Widget>(m, "Widget" ) .def ("foo_mutable" , py::overload_cast <int , float >(&Widget::foo)) .def ("foo_const" , py::overload_cast <int , float >(&Widget::foo, py::const_));
4. C++11兼容:py::detail::overload_cast_impl
仅支持C++11的编译器可通过模板别名overload_cast_(基于py::detail::overload_cast_impl)实现类似py::overload_cast的功能,语法稍繁琐。
1 2 3 4 5 6 template <typename ... Args>using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>; py::class_ <Pet>(m, "Pet" ) .def ("set" , overload_cast_ <int >()(&Pet::set), "Set the pet's age" ) .def ("set" , overload_cast_ <const std::string &>()(&Pet::set), "Set the pet's name" );
重载构造函数的绑定
对于重载的构造函数,直接多次使用.def(py::init<...>())即可,每次指定不同的参数列表,支持关键字参数和默认参数。
类绑定
基本类绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <pybind11/pybind11.h> #include <string> namespace py = pybind11;class Person {public : Person (const std::string& name, int age): name (name), age (age) {} void set_name (const std::string& new_name) { } std::string get_name () const { } int get_age () const { } std::string greet () const {} private : std::string name; int age; }; PYBIND11_MODULE (example, m) { py::class_ <Person>(m, "Person" ) .def (py::init <const std::string&, int >(), py::arg ("name" ), py::arg ("age" )) .def ("set_name" , &Person::set_name,py::arg ("new_name" )) .def ("get_name" , &Person::get_name) .def ("get_age" , &Person::get_age) .def ("greet" , &Person::greet) .def_property ("name" ,&Person::get_name,&Person::set_name); def_readwrite ("name" , &Person::name) def_readonly ("age" , &Person::age); }
Python使用:
1 2 3 4 5 person = example.Person("Alice" , 30 ) print (person.get_name()) print (person.greet()) person.set_name("Bob" ) print (person.name)
继承和多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <pybind11/pybind11.h> #include <string> namespace py = pybind11;class Animal {public : virtual ~Animal () {} virtual std::string speak () const = 0 ; virtual int get_legs () const = 0 ; }; class Dog : public Animal {public : std::string speak () const override {return "Woof!" ;} int get_legs () const override { return 4 ; } void fetch () const { }; class Bird : public Animal {public : std::string speak () const override {return "Chirp!" ;} int get_legs () const override {return 2 ;} void fly () const { }; PYBIND11_MODULE (example, m) { py::class_ <Animal>(m, "Animal" ) .def ("speak" , &Animal::speak) .def ("get_legs" , &Animal::get_legs); py::class_ <Dog, Animal>(m, "Dog" ) .def (py::init<>()) .def ("fetch" , &Dog::fetch); py::class_ <Bird, Animal>(m, "Bird" ) .def (py::init<>()) .def ("fly" , &Bird::fly); }
虚函数重写(蹦床类)
见另一份指南
STL容器支持
基础容器转换
pybind11通过pybind11/stl.h头文件提供STL容器的自动转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <vector> #include <map> #include <set> #include <string> namespace py = pybind11;std::vector<int > process_vector (const std::vector<int >& input) { std::vector<int > result; for (int num : input) { result.push_back (num * 2 ); } return result; } std::set<std::string> unique_words (const std::vector<std::string>& words) { return std::set <std::string>(words.begin (), words.end ()); } std::map<std::string, int > count_chars (const std::string& str) { std::map<std::string, int > result; for (char c : str) { result[std::string (1 , c)]++; } return result; } PYBIND11_MODULE (example, m) { m.def ("process_vector" , &process_vector, "Double each element in vector" ); m.def ("unique_words" , &unique_words, "Get unique words from list" ); m.def ("count_chars" , &count_chars, "Count character occurrences" ); }
Python使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 import exampleresult = example.process_vector([1 , 2 , 3 , 4 ]) print (result) unique = example.unique_words(["apple" , "banana" , "apple" , "orange" ]) print (unique) counts = example.count_chars("hello world" ) print (counts)
嵌套容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <vector> #include <map> #include <set> #include <string> namespace py = pybind11;std::vector<std::map<std::string, int >> process_nested_data ( const std::vector<std::map<std::string, int >>& data) { std::vector<std::map<std::string, int >> result; for (const auto & map_data : data) { std::map<std::string, int > processed_map; for (const auto & [key, value] : map_data) { processed_map[key] = value * 2 ; } result.push_back (processed_map); } return result; } std::map<std::string, std::vector<std::set<int >>> complex_structure () { return { {"even" , {{2 , 4 , 6 }, {8 , 10 , 12 }}}, {"odd" , {{1 , 3 , 5 }, {7 , 9 , 11 }}} }; } PYBIND11_MODULE (example, m) { m.def ("process_nested_data" , &process_nested_data, "Process nested data structures" ); m.def ("complex_structure" , &complex_structure, "Create complex nested structure" ); }
C++17容器支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <optional> #include <variant> #include <string> namespace py = pybind11;std::optional<int > find_first_even (const std::vector<int >& numbers) { for (int num : numbers) { if (num % 2 == 0 ) { return num; } } return std::nullopt ; } using Value = std::variant<int , double , std::string>;std::string value_type (const Value& value) { return std::visit ([](auto && arg) -> std::string { using T = std::decay_t <decltype (arg)>; if constexpr (std::is_same_v<T, int >) return "integer" ; else if constexpr (std::is_same_v<T, double >) return "double" ; else if constexpr (std::is_same_v<T, std::string>) return "string" ; else return "unknown" ; }, value); } PYBIND11_MODULE (example, m) { m.def ("find_first_even" , &find_first_even, "Find first even number" ); m.def ("value_type" , &value_type, "Get type of variant value" ); }
高级特性
异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <pybind11/pybind11.h> #include <stdexcept> #include <string> namespace py = pybind11;int divide (int a, int b) { if (b == 0 ) { throw std::runtime_error ("Division by zero is not allowed" ); } return a / b; } void validate_age (int age) { if (age < 0 ) { throw std::invalid_argument ("Age cannot be negative" ); } if (age > 150 ) { throw std::out_of_range ("Age is too large" ); } } PYBIND11_MODULE (example, m) { m.def ("divide" , ÷, "Divide two integers" ); m.def ("validate_age" , &validate_age, "Validate age range" ); }
Python中处理异常:
1 2 3 4 5 6 7 8 9 10 11 import exampletry : result = example.divide(10 , 0 ) except RuntimeError as e: print (f"Error: {e} " ) try : example.validate_age(-5 ) except ValueError as e: print (f"Validation error: {e} " )
自定义类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <string> namespace py = pybind11;struct Point { int x; int y; Point (int x_ = 0 , int y_ = 0 ) : x (x_), y (y_) {} Point operator +(const Point& other) const { return Point (x + other.x, y + other.y); } std::string to_string () const { return "(" + std::to_string (x) + ", " + std::to_string (y) + ")" ; } }; namespace pybind11 { namespace detail { template <> struct type_caster <Point> { public : PYBIND11_TYPE_CASTER (Point, _("Point" )); bool load (handle src, bool ) { if (!src) return false ; auto obj = reinterpret_borrow <dict>(src); value.x = obj["x" ].cast <int >(); value.y = obj["y" ].cast <int >(); return true ; } static handle cast (Point src, return_value_policy , handle ) { dict d; d["x" ] = src.x; d["y" ] = src.y; return d.release (); } }; }} PYBIND11_MODULE (example, m) { py::class_ <Point>(m, "Point" ) .def (py::init <int , int >(), py::arg ("x" ) = 0 , py::arg ("y" ) = 0 ) .def ("__add__" , &Point::operator +) .def ("to_string" , &Point::to_string) .def_readwrite ("x" , &Point::x) .def_readwrite ("y" , &Point::y); }
NumPy集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <pybind11/pybind11.h> #include <pybind11/numpy.h> #include <vector> namespace py = pybind11;py::array_t <double > vector_to_numpy (const std::vector<double >& vec) { return py::array_t <double >(vec.size (), vec.data ()); } std::vector<double > numpy_to_vector (const py::array_t <double >& array) { std::vector<double > result (array.size()) ; std::memcpy (result.data (), array.data (), array.size () * sizeof (double )); return result; } void multiply_array_inplace (py::array_t <double >& array, double factor) { py::buffer_info buf = array.request (); double * ptr = static_cast <double *>(buf.ptr); for (size_t i = 0 ; i < buf.size; i++) { ptr[i] *= factor; } } PYBIND11_MODULE (example, m) { m.def ("vector_to_numpy" , &vector_to_numpy, "Convert vector to NumPy array" ); m.def ("numpy_to_vector" , &numpy_to_vector, "Convert NumPy array to vector" ); m.def ("multiply_array_inplace" , &multiply_array_inplace, "Multiply array elements in-place" ); }
嵌入Python解释器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <pybind11/embed.h> #include <iostream> #include <string> namespace py = pybind11;int main () { py::scoped_interpreter guard{}; py::print ("Hello from embedded Python!" ); py::module_ math = py::module_::import ("math" ); py::object result = math.attr ("sqrt" )(25 ); std::cout << "Square root of 25 is " << result.cast <double >() << std::endl; py::exec (R"( def add(a, b): return a + b result = add(3, 5) print(f"3 + 5 = {result}") )" ); PYBIND11_EMBEDDED_MODULE (fast_ops, m) { m.def ("multiply" , [](int a, int b) { return a * b; }); } auto fast_ops = py::module_::import ("fast_ops" ); int product = fast_ops.attr ("multiply" )(4 , 6 ).cast <int >(); std::cout << "4 * 6 = " << product << std::endl; return 0 ; }
跨平台构建
CMake配置
见另一份指南
命令行编译
Linux/macOS
1 2 3 4 5 6 7 8 9 10 11 12 mkdir buildcd buildcmake .. -DCMAKE_BUILD_TYPE=Release make -j4 sudo make install
Windows (Visual Studio)
1 2 3 4 5 6 7 8 9 10 11 12 mkdir buildcd buildcmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release --target ALL_BUILD cmake --build . --config Release --target INSTALL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from setuptools import setup, Extensionfrom pybind11.setup_helpers import Pybind11Extension, build_extimport sysext_modules = [ Pybind11Extension( "my_module" , ["src/main.cpp" , "src/utils.cpp" ], include_dirs=["include" ], extra_compile_args=["-std=c++17" ], language="c++" , ), ] setup( name="my_module" , version="1.0.0" , author="Your Name" , author_email="your.email@example.com" , description="A pybind11 example module" , long_description="" , ext_modules=ext_modules, cmdclass={"build_ext" : build_ext}, zip_safe=False , python_requires=">=3.8" , )
使用setup.py构建:
1 python setup.py build_ext --inplace
绑定代码分离
将C++实现和pybind11绑定代码分离:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #pragma once #include <vector> #include <string> namespace my_module { std::vector<double > process_data (const std::vector<double >& input) ; std::string format_result (double value) ; } #include "core.h" #include <algorithm> namespace my_module { std::vector<double > process_data (const std::vector<double >& input) { std::vector<double > result = input; std::transform (result.begin (), result.end (), result.begin (), [](double x) { return x * 2.0 ; }); return result; } std::string format_result (double value) { return "Result: " + std::to_string (value); } } #include <pybind11/pybind11.h> #include <pybind11/stl.h> #include "core.h" namespace py = pybind11;PYBIND11_MODULE (my_module, m) { m.doc () = "My module documentation" ; m.def ("process_data" , &my_module::process_data, "Process input data" ); m.def ("format_result" , &my_module::format_result, "Format result as string" ); }
常见问题解决
1. 编译错误
问题:头文件找不到
1 fatal error: pybind11/pybind11.h: No such file or directory
解决方案:
1 2 3 4 5 c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) c++ -O3 -Wall -shared -std=c++11 -fPIC -I/path/to/pybind11/include example.cpp -o example.so
问题:Python版本不匹配
1 undefined symbol: PyExc_RuntimeError
解决方案:
确保使用的Python版本与编译时使用的版本一致。
1 2 3 4 5 6 python3 --version python3-config --includes python3-config --extension-suffix
2. 运行时错误
问题:模块导入失败
1 ImportError: No module named 'example'
解决方案:
1 2 3 4 5 6 7 8 ls -la example*python3 -c "import sys; print(sys.path)" export PYTHONPATH=$PYTHONPATH :/path/to/module
问题:类型转换错误
1 TypeError: incompatible types, c++ type is std::vector<int >
解决方案:
确保包含STL头文件:
1 #include <pybind11/stl.h>
3. 性能问题
问题:大数据传输缓慢
解决方案:
使用NumPy数组避免数据拷贝:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <pybind11/numpy.h> py::array_t <double > process_large_data (py::array_t <double >& input) { py::buffer_info buf = input.request (); double * ptr = static_cast <double *>(buf.ptr); for (size_t i = 0 ; i < buf.size; i++) { ptr[i] = ptr[i] * 2.0 ; } return input; }
4. 跨平台问题
问题:Windows上链接错误
1 error LNK2001: unresolved external symbol
解决方案:
使用正确的Visual Studio版本和架构:
1 2 3 4 5 cmake .. -G "Visual Studio 17 2022" -A x64 python -c "import platform; print(platform.architecture())"
问题:macOS上符号冲突
解决方案:
使用dynamic_lookup:
1 2 3 if (APPLE) target_link_options (example PRIVATE -undefined dynamic_lookup) endif ()
5. 调试技巧
启用调试信息
1 2 3 4 5 6 cmake .. -DCMAKE_BUILD_TYPE=Debug make cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Debug
使用GDB调试
1 2 3 4 gdb python3 (gdb) run -c "import example; example.add(1, 2)" (gdb) break example.cpp:10 (gdb) continue
输出调试信息
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <pybind11/pybind11.h> #include <iostream> namespace py = pybind11;int add (int a, int b) { std::cout << "Adding " << a << " and " << b << std::endl; return a + b; } PYBIND11_MODULE (example, m) { m.def ("add" , &add); }