Skip to content

第17章 模块组织

大型项目的绑定代码需要模块化组织,将不同功能域的绑定分散到多个文件中,并通过子模块和命名空间进行层次化管理。

将绑定代码按逻辑功能域拆分到不同文件,保持代码清晰。

// math_bindings.cpp - 数学相关绑定
#include <pybind11/pybind11.h>
#include "math_utils.h"
namespace py = pybind11;
// 导出数学工具函数
py::object add_numbers(py::object a, py::object b) {
py::object result = py::cast(py::int_(py::cast<int>(a)) + py::int_(py::cast<int>(b)));
return result;
}
py::object multiply_numbers(py::object a, py::object b) {
return py::cast(py::int_(py::cast<int>(a) * py::cast<int>(b)));
}
double calculate_circle_area(double radius) {
return 3.14159265358979 * radius * radius;
}
double calculate_sphere_volume(double radius) {
return (4.0 / 3.0) * 3.14159265358979 * radius * radius * radius;
}
// 独立模块声明,供 main 模块调用
void bind_math(py::module_& m) {
m.def("add", &add_numbers, "Add two numbers");
m.def("multiply", &multiply_numbers, "Multiply two numbers");
m.def("circle_area", &calculate_circle_area, "Calculate circle area");
m.def("sphere_volume", &calculate_sphere_volume, "Calculate sphere volume");
}
// string_bindings.cpp - 字符串处理绑定
#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;
std::string trim_string(const std::string& s) {
size_t start = s.find_first_not_of(" \t\n\r");
size_t end = s.find_last_not_of(" \t\n\r");
if (start == std::string::npos) return "";
return s.substr(start, end - start + 1);
}
std::string to_upper_case(const std::string& s) {
std::string result = s;
for (auto& c : result) {
c = std::toupper(c);
}
return result;
}
bool starts_with(const std::string& s, const std::string& prefix) {
return s.substr(0, prefix.length()) == prefix;
}
void bind_strings(py::module_& m) {
m.def("trim", &trim_string, "Trim whitespace from string");
m.def("upper", &to_upper_case, "Convert to uppercase");
m.def("starts_with", &starts_with, "Check if string starts with prefix");
}
// main_module.cpp - 主模块,聚合所有子模块绑定
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 声明子模块绑定函数
void bind_math(py::module_& m);
void bind_strings(py::module_& m);
PYBIND11_MODULE(mylib, m) {
m.doc() = "my_library - A multi-file module example";
// 创建子模块:math
py::module_ math_submodule = m.def_submodule("math", "Math utilities submodule");
bind_math(math_submodule);
// 创建子模块:strings
py::module_ string_submodule = m.def_submodule("strings", "String utilities submodule");
bind_strings(string_submodule);
}
>>> import mylib
>>> # 通过子模块访问
>>> mylib.math.add(3, 5)
8
>>> mylib.math.multiply(4, 7)
28
>>> mylib.math.circle_area(5.0)
78.53981633974483
>>> # 字符串子模块
>>> mylib.strings.trim(" hello world ")
'hello world'
>>> mylib.strings.upper("hello")
'HELLO'
>>> mylib.strings.starts_with("filename.txt", "file")
True

关键洞察:将绑定代码拆分到多个 .cpp 文件,按功能域组织。主模块通过 def_submodule() 创建子模块,再将各功能绑定聚合进去。

嵌套子模块构建层级化的命名空间,便于组织和访问。

nested_modules.cpp
#include <pybind11/pybind11.h>
#include <string>
#include <vector>
namespace py = pybind11;
// 层级化模块结构示例
// mylib/
// ├── core/ # 核心功能
// │ ├── utils/ # 工具函数
// │ └── config/ # 配置管理
// ├── algorithms/ # 算法模块
// └── data/ # 数据处理
// 核心工具子模块
void bind_core_utils(py::module_& m) {
m.def("version", []() { return "1.0.0"; });
m.def("get_timestamp", []() { return 1699999999; });
}
// 核心配置子模块
void bind_core_config(py::module_& m) {
m.def("get", [](const std::string& key) {
if (key == "debug") return false;
if (key == "max_retries") return 3;
return py::none();
});
m.def("set", [](const std::string& key, py::object value) {
py::print("Setting", key, "=", value);
});
}
// 算法子模块
void bind_algorithms(py::module_& m) {
m.def("sort", [](std::vector<int> v) {
std::sort(v.begin(), v.end());
return v;
});
m.def("binary_search", [](const std::vector<int>& v, int target) {
auto it = std::lower_bound(v.begin(), v.end(), target);
if (it != v.end() && *it == target) {
return static_cast<int>(std::distance(v.begin(), it));
}
return -1;
});
}
PYBIND11_MODULE(hierarchical_lib, m) {
m.doc() = "hierarchical_lib - Library with nested submodules";
// 方式1:通过 def_submodule 链式创建
py::module_ core = m.def_submodule("core", "Core functionality");
py::module_ core_utils = core.def_submodule("utils", "Utility functions");
py::module_ core_config = core.def_submodule("config", "Configuration management");
bind_core_utils(core_utils);
bind_core_config(core_config);
// 方式2:直接创建算法模块
py::module_ algorithms = m.def_submodule("algorithms", "Algorithm implementations");
bind_algorithms(algorithms);
// 方式3:使用 py::module_ 构造函数创建子模块
py::module_ data = py::module_::create_extension_module("data", "Data processing", m.ptr());
data.def("process", [](py::list items) {
return py::cast(py::list(items));
});
}
>>> import hierarchical_lib as lib
>>> # 层级访问
>>> lib.core.utils.version()
'1.0.0'
>>> lib.core.config.get("debug")
False
>>> lib.core.config.set("timeout", 30)
Setting timeout = 30
>>> # 算法模块
>>> lib.algorithms.sort([3, 1, 4, 1, 5])
[1, 1, 3, 4, 5]
>>> lib.algorithms.binary_search([1, 3, 5, 7], 5)
2
>>> # 数据模块
>>> lib.data.process([1, 2, 3])
[1, 2, 3]

关键洞察:def_submodule() 创建命名子模块。若需要更灵活的模块创建,可使用 py::module_::create_extension_module() 从 C++ 端动态创建模块对象。

py::module_ vs C++ namespace 的对应关系。

namespace_handling.cpp
#include <pybind11/pybind11.h>
#include <string>
#include <vector>
namespace py = pybind11;
// 模拟 C++ 命名空间组织
namespace math {
namespace operations {
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return a / b; }
}
namespace advanced {
double power(double base, double exp) {
double result = 1.0;
for (int i = 0; i < static_cast<int>(exp); ++i) {
result *= base;
}
return result;
}
double sqrt(double x) {
if (x < 0) return 0;
double guess = x / 2.0;
for (int i = 0; i < 20; ++i) {
guess = (guess + x / guess) / 2.0;
}
return guess;
}
}
}
// 使用 py::module_ 映射 C++ 命名空间结构
PYBIND11_MODULE(namespace_demo, m) {
m.doc() = "namespace_demo - Demonstrating namespace mapping";
// 方法1:使用 def_submodule 映射命名空间层级
py::module_ math_mod = m.def_submodule("math", "Math namespace");
py::module_ ops_mod = math_mod.def_submodule("operations", "Basic operations");
py::module_ adv_mod = math_mod.def_submodule("advanced", "Advanced operations");
// C++ math::operations -> Python math.operations
ops_mod.def("add", &math::operations::add, "Addition");
ops_mod.def("multiply", &math::operations::multiply, "Multiplication");
ops_mod.def("divide", &math::operations::divide, "Division");
// C++ math::advanced -> Python math.advanced
adv_mod.def("power", &math::advanced::power, "Power function");
adv_mod.def("sqrt", &math::advanced::sqrt, "Square root");
// 方法2:直接在模块上定义,用 docstring 说明命名空间
py::module_ util_mod = m.def_submodule("util", "Utilities (flat namespace)");
util_mod.def("add", &math::operations::add, "Addition (in util namespace)");
util_mod.def("sqrt", &math::advanced::sqrt, "Sqrt (in util namespace)");
}
>>> import namespace_demo as m
>>> # 层级化命名空间(推荐)
>>> m.math.operations.add(10, 5)
15
>>> m.math.operations.multiply(6, 7)
42
>>> m.math.advanced.power(2, 8)
256.0
>>> m.math.advanced.sqrt(16)
4.0
>>> # 扁平命名空间(备选)
>>> m.util.add(3, 4)
7
>>> m.util.sqrt(25)
5.0

关键洞察:C++ 命名空间结构可通过 def_submodule() 层级映射到 Python 模块层级。保持与 C++ 源码结构一致可提高可维护性。

PYBIND11_MODULE vs 已废弃的 PYBIND11_PLUGIN 宏。

module_init.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// ============================================
// 当前推荐方式:PYBIND11_MODULE
// ============================================
// PYBIND11_MODULE 是当前推荐的标准方式
// 宏参数:(模块名, 变量名)
// - 模块名:Python 中 import 的名称
// - 变量名:py::module_ 类型的局部变量
PYBIND11_MODULE(recommended_module, m) {
m.doc() = "recommended_module - Using PYBIND11_MODULE";
m.def("greet", []() { return "Hello from recommended_module!"; });
m.def("version", []() { return "1.0"; });
}
// ============================================
// 已废弃方式:PYBIND11_PLUGIN
// ============================================
// PYBIND11_PLUGIN 是旧版宏,已废弃,不推荐使用
// 存在兼容性问题,未来版本可能移除
// 以下代码仅作演示,请使用 PYBIND11_MODULE
// 旧版写法(废弃):
// PYBIND11_PLUGIN(myplugin, m) {
// m.def("old_greet", []() { return "Hello from old plugin!"; });
// }
>>> import recommended_module
>>> recommended_module.greet()
'Hello from recommended_module!'
>>> recommended_module.version()
'1.0'

关键洞察:PYBIND11_MODULE 是当前标准写法。PYBIND11_PLUGIN 已被废弃,请勿在新代码中使用。

在模块中记录和暴露版本元数据。

version_info.cpp
#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;
// 集中管理版本信息
#define VERSION_MAJOR 2
#define VERSION_MINOR 1
#define VERSION_PATCH 0
#define VERSION_STRING "2.1.0"
// 通过函数返回版本信息
py::dict get_version_info() {
return py::dict(
py::arg("major") = VERSION_MAJOR,
py::arg("minor") = VERSION_MINOR,
py::arg("patch") = VERSION_PATCH,
py::arg("string") = VERSION_STRING,
py::arg("full") = VERSION_STRING
);
}
// 返回构建信息
py::dict get_build_info() {
return py::dict(
py::arg("compiler") = "clang",
py::arg("build_type") = "release",
py::arg("pybind11_version") = PYBIND11_VERSION
);
}
// 返回所有依赖版本
py::dict get_dependencies() {
return py::dict(
py::arg("pybind11") = PYBIND11_VERSION,
py::arg("python") = ">=3.8"
);
}
PYBIND11_MODULE(versioned_lib, m) {
m.doc() = "versioned_lib - Library with version information";
// 模块级版本属性
m.attr("__version__") = VERSION_STRING;
m.attr("__author__") = "Your Name";
m.attr("__license__") = "MIT";
// 版本查询函数
m.def("version", &get_version_info, "Get version information");
m.def("build_info", &get_build_info, "Get build information");
m.def("dependencies", &get_dependencies, "Get dependency versions");
// 便捷访问函数
m.def("version_string", []() { return VERSION_STRING; });
m.def("version_tuple", []() {
return py::make_tuple(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
});
}
>>> import versioned_lib as lib
>>> # 模块属性
>>> lib.__version__
'2.1.0'
>>> lib.__author__
'Your Name'
>>> lib.__license__
'MIT'
>>> # 版本信息函数
>>> lib.version()
{'major': 2, 'minor': 1, 'patch': 0, 'string': '2.1.0', 'full': '2.1.0'}
>>> lib.version_string()
'2.1.0'
>>> lib.version_tuple()
(2, 1, 0)
>>> # 构建信息
>>> lib.build_info()
{'compiler': 'clang', 'build_type': 'release', 'pybind11_version': '...'}
>>> # 依赖版本
>>> lib.dependencies()
{'pybind11': '...', 'python': '>=3.8'}

关键洞察:通过 m.attr() 设置模块级属性(__version__、__author__ 等),提供一致的版本信息访问接口。版本信息集中管理便于发布和维护。

模块组织总结:

技术用途关键函数
多文件结构按功能域拆分绑定代码独立 .cpp 文件 + 主模块调用
子模块创建层级化模块结构py::module_::def_submodule()
命名空间映射对应 C++ 命名空间到 Pythondef_submodule() 链式调用
模块初始化定义 Python 模块入口PYBIND11_MODULE(推荐)
版本信息记录模块版本元数据m.attr() + 版本函数

实战建议:大型项目将绑定按 C++ 类或功能域拆分到独立文件,主模块聚合子模块。始终使用 PYBIND11_MODULE,避免废弃的 PYBIND11_PLUGIN。