Skip to content

第28章 嵌入Python解释器

嵌入(Embedding)是将 Python 解释器集成到 C++ 程序中的技术。这是绑定的逆过程——我们不是把 C++ 代码暴露给 Python,而是把 Python 解释器嵌入到 C++ 应用中。

为什么要嵌入 Python 解释器?常见的应用场景包括:

  • 插件系统:允许用户使用 Python 编写插件扩展应用功能
  • 脚本引擎:为游戏或仿真器提供脚本能力
  • 测试框架:在 C++ 程序中运行 Python 测试代码
  • 配置系统:使用 Python 脚本作为配置文件

嵌入与绑定的核心区别:

方向绑定(Binding)嵌入(Embedding)
Python → C++Python 调用 C++C++ 调用 Python
入口PYBIND11_MODULEPy_Initialize
目的暴露 C++ 给 Python在 C++ 中运行 Python

使用 Py_Initialize() 和 Py_Finalize() 管理 Python 解释器生命周期。

#include <pybind11/embed.h>
#include <iostream>
#include <string>
namespace py = pybind11;
int main(int argc, char* argv[]) {
// 初始化 Python 解释器
// 必须在使用任何 Python API 之前调用
py::scoped_interpreter guard{};
// 可选:传递命令行参数给 Python
PySys_SetArgv(argc, argv);
// 现在可以执行 Python 代码
std::cout << "Python interpreter embedded successfully!" << std::endl;
// Python 解释器在 guard 对象销毁时自动清理
return 0;
}
// 更细粒度的控制:手动初始化和关闭
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
// 检查是否需要初始化
if (!Py_IsInitialized()) {
std::cout << "Initializing Python..." << std::endl;
Py_Initialize();
}
// 检查初始化状态
if (Py_IsInitialized()) {
std::cout << "Python interpreter ready" << std::endl;
// 执行一些代码
py::exec("print('Hello from embedded Python!')");
// 关闭解释器
Py_Finalize();
std::cout << "Python interpreter closed" << std::endl;
} else {
std::cerr << "Failed to initialize Python" << std::endl;
return 1;
}
return 0;
}

关键洞察:py::scoped_interpreter 是最安全的初始化方式——它使用 RAII 模式确保解释器正确关闭。在生产代码中,优先使用这种方式。

使用 py::exec 和 py::eval 在 C++ 中执行 Python 代码。

#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
// 执行单行代码
py::exec("result = 2 + 3");
py::exec("print('2 + 3 =', result)");
// 执行包含控制流的代码块
py::exec(R"(
for i in range(5):
print(f"iteration {i}")
)");
// 求值单个表达式
py::object result = py::eval("42 * 2");
std::cout << "42 * 2 = " << py::cast<int>(result) << std::endl;
// 求值带作用域的表达式
py::object locals = py::dict();
py::exec("x = 100", py::globals(), locals);
py::object x = py::eval("x * 2", py::globals(), locals);
std::cout << "x * 2 = " << py::cast<int>(x) << std::endl;
return 0;
}
// 使用 py::module_ 执行模块级代码
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
// 获取 builtins 模块
py::object builtins = py::module_::import("builtins");
py::object result = builtins.attr("sum")(py::make_tuple(1, 2, 3, 4, 5));
std::cout << "sum([1,2,3,4,5]) = " << py::cast<int>(result) << std::endl;
// 使用 py::exec_file 执行 Python 文件
// py::exec_file("script.py");
return 0;
}

关键洞察:py::eval 用于表达式(返回结果),py::exec 用于语句(无返回值)。两者都可以接受 py::globals() 和 py::dict() 作为作用域参数来控制变量访问。

实现 C++ 和 Python 之间的数据传递和函数调用。

#include <pybind11/embed.h>
#include <iostream>
#include <string>
#include <vector>
namespace py = pybind11;
// C++ 函数:可以被 Python 调用
int add_numbers(int a, int b) {
return a + b;
}
// C++ 类:可以被 Python 使用
class Calculator {
public:
Calculator() = default;
double compute(double x, double y, const std::string& op) {
if (op == "add") return x + y;
if (op == "sub") return x - y;
if (op == "mul") return x * y;
if (op == "div" && y != 0) return x / y;
return 0;
}
};
PYBIND11_EMBEDDED_MODULE(embedded_module, m) {
// 导出 C++ 函数到 Python
m.def("add", &add_numbers, "Add two integers");
// 导出 C++ 类到 Python
py::class_<Calculator>(m, "Calculator")
.def(py::init<>())
.def("compute", &Calculator::compute, "Perform calculation");
}
int main() {
py::scoped_interpreter guard{};
// 导入嵌入式模块(在 C++ 中注册)
py::module_ embedded_module = py::module_::import("embedded_module");
// 从 C++ 调用 Python 函数
py::object python_func = py::eval("lambda x: x * 2");
py::object cpp_result = python_func(42);
std::cout << "Python lambda(42) = " << py::cast<int>(cpp_result) << std::endl;
// 使用 C++ 导出给 Python 的函数
py::object cpp_result2 = embedded_module.attr("add")(10, 20);
std::cout << "C++ add(10, 20) = " << py::cast<int>(cpp_result2) << std::endl;
// 使用 C++ 导出给 Python 的类
py::object calc = embedded_module.attr("Calculator")();
py::object result = calc.attr("compute")(10.0, 3.0, py::str("mul"));
std::cout << "Calculator.compute(10, 3, 'mul') = " << py::cast<double>(result) << std::endl;
// 从 C++ 设置 Python 全局变量
py::globals()["cpp_var"] = py::int_(42);
py::exec("print('cpp_var from Python:', cpp_var)");
// 从 Python 获取变量到 C++
py::exec("python_result = 123");
int result_value = py::cast<int>(py::globals()["python_result"]);
std::cout << "python_result from C++ = " << result_value << std::endl;
// 传递复杂数据结构
py::list cpp_list = py::cast(std::vector<int>{1, 2, 3, 4, 5});
py::exec("print('List from C++:', cpp_list)", py::globals(), py::dict(py::arg("cpp_list") = cpp_list));
return 0;
}
import embedded_module
result = embedded_module.add(3, 4)
print(f"3 + 4 = {result}")
calc = embedded_module.Calculator()
print(f"10 + 5 = {calc.compute(10, 5, 'add')}")
print(f"10 - 5 = {calc.compute(10, 5, 'sub')}")
print(f"10 * 5 = {calc.compute(10, 5, 'mul')}")
print(f"10 / 5 = {calc.compute(10, 5, 'div')}")

关键洞察:嵌入模式下的双向通信需要仔细管理对象生命周期。使用 py::cast<T>() 和 py::make_tuple() / py::dict() 等工具在不同语言之间传递数据。

#include <pybind11/embed.h>
#include <iostream>
#include <map>
#include <string>
#include <memory>
namespace py = pybind11;
// 插件接口
class IPlugin {
public:
virtual ~IPlugin() = default;
virtual std::string name() const = 0;
virtual void execute() = 0;
};
// 插件管理器
class PluginManager {
public:
void load_plugin(const std::string& plugin_path) {
py::object importlib = py::module_::import("importlib.util");
py::object spec = importlib.attr("spec_from_file_location")(
"plugin_module", py::str(plugin_path));
py::object module = importlib.attr("module_from_spec")(spec);
spec.attr("loader").attr("exec_module")(module);
// 获取插件类并实例化
py::object plugin_class = module.attr("Plugin");
py::object plugin_instance = plugin_class();
plugins_[plugin_instance.attr("name").cast<std::string>()] = plugin_instance;
}
void execute_plugin(const std::string& name) {
if (plugins_.contains(name)) {
plugins_[name].attr("run")();
}
}
void list_plugins() {
py::exec("print('Loaded plugins:', list(plugins.keys()))",
py::dict(py::arg("plugins") = py::cast(plugins_)));
}
private:
std::map<std::string, py::object> plugins_;
};
class Plugin:
name = "ExamplePlugin"
version = "1.0.0"
def run(self):
print(f"Running {self.name} v{self.version}")
def process(self, data):
return f"Processed: {data}"
#include <pybind11/embed.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
namespace py = pybind11;
class ScriptEngine {
public:
ScriptEngine() {
py::exec(R"(
import math
from typing import List
class ScriptEngine:
def __init__(self):
self.variables = {}
def set_variable(self, name: str, value: float) -> None:
self.variables[name] = value
def get_variable(self, name: str) -> float:
return self.variables.get(name, 0.0)
def evaluate(self, expr: str) -> float:
# 安全评估数学表达式
allowed_names = {**math.__dict__, **self.variables}
return eval(expr, {"__builtins__": {}}, allowed_names)
)");
}
void set_variable(const std::string& name, double value) {
py::exec(R"(
scope = {'name': name, 'value': value}
exec(f'{name} = {value}', scope)
)",
py::dict(py::arg("name") = py::str(name),
py::arg("value") = py::float_(value)));
}
double evaluate(const std::string& expression) {
py::object result = py::eval(expression);
return result.cast<double>();
}
private:
py::object engine_;
};
// 简化的数学表达式求值
double eval_math(const std::string& expr, const std::map<std::string, double>& vars) {
py::scoped_interpreter guard{};
// 设置变量到 Python 空间
py::dict local_dict;
for (const auto& [k, v] : vars) {
local_dict[k.c_str()] = py::float_(v);
}
py::object result = py::eval(expr.c_str(), py::dict(), local_dict);
return result.cast<double>();
}
#include <pybind11/embed.h>
#include <iostream>
#include <string>
#include <vector>
namespace py = pybind11;
struct TestResult {
std::string name;
bool passed;
std::string message;
};
class PythonTestRunner {
public:
PythonTestRunner() {
// 配置 pytest
py::exec(R"(
import sys
def run_tests():
import pytest
# 配置 pytest 输出
pytest.main(['-v', '--tb=short'])
)");
}
void run_test_file(const std::string& test_file) {
py::module_ pytest = py::module_::import("pytest");
pytest.attr("main")(py::make_tuple("-v", "--tb=short", "-s", test_file.c_str()));
}
std::vector<TestResult> discover_tests(const std::string& module_path) {
py::object pytest = py::module_::import("pytest");
py::object collector = pytest.attr("Collector");
std::vector<TestResult> results;
// 发现和运行测试的逻辑
return results;
}
};
// 在 C++ 中使用 pytest
int main(int argc, char* argv[]) {
py::scoped_interpreter guard{};
PythonTestRunner runner;
// 运行指定的测试文件
if (argc > 1) {
std::cout << "Running tests from: " << argv[1] << std::endl;
runner.run_test_file(argv[1]);
}
return 0;
}

关键洞察:嵌入 Python 为 C++ 应用提供了极大的灵活性。插件系统允许用户扩展应用功能,脚本引擎让非 C++ 开发者也能参与业务逻辑,测试框架则能复用 Python 丰富的测试工具生态。

嵌入Python总结:

组件说明
py::scoped_interpreterRAII 风格的解释器初始化
py::exec()执行 Python 语句块
py::eval()求值 Python 表达式
py::module_::import()导入 Python 模块
py::globals()访问 Python 全局命名空间
py::dict()创建 Python 字典
PYBIND11_EMBEDDED_MODULE在 C++ 中注册模块供 Python 导入

最佳实践:

  1. 始终使用 py::scoped_interpreter 管理生命周期
  2. 在多线程环境中注意 GIL 管理
  3. 使用 py::gil_scoped_release 释放 GIL 以提高性能
  4. 避免在 Python 代码中执行耗时的 CPU 密集型操作