第15章 函数式编程支持
pybind11 提供了丰富的函数式编程支持,让 C++ 的 lambda、函数对象和 std::function 可以无缝转换为 Python 可调用对象。
15.1 Lambda 函数绑定
Section titled “15.1 Lambda 函数绑定”C++ lambda 表达式可以直接暴露给 Python 作为可调用对象。
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 捕获空白的 lambdaauto square = [](int x) { return x * x; };
// 捕获单个值的 lambdaauto make_adder(int delta) { return [delta](int x) { return x + delta; };}
// 捕获 Python 对象的 lambda(需要 GIL)py::object make_processor(py::object target) { return py::cpp_function([target](int x) { py::gil_scoped_acquire gil; // 调用 Python 对象 return target.attr("process")(x); });}
PYBIND11_MODULE(lambda_module, m) { // 暴露简单 lambda m.def("square", square);
// 暴露工厂函数返回的 lambda m.def("make_adder", &make_adder);
// 暴露返回 callable 的函数 m.def("make_processor", &make_processor);}>>> import lambda_module as m>>> m.square(5)25
>>> adder = m.make_adder(10)>>> adder(5)15>>> adder(100)110
>>> class Processor:... def process(self, x):... return x * 2
>>> proc = m.make_processor(Processor())>>> proc(21)42关键洞察:无捕获的 lambda 可以直接转换为 Python 函数。有捕获的 lambda 在调用时需要确保 GIL 可用,尤其是捕获了 Python 对象的情况下。
15.2 std::function 绑定
Section titled “15.2 std::function 绑定”std::function 可以存储任意可调用对象,并在 Python 端暴露为回调。
#include <pybind11/pybind11.h>#include <functional>
namespace py = pybind11;
// 使用 std::function 作为参数void apply_function(const std::function<int(int)>& f, int value) { if (f) { value = f(value); }}
py::object call_function(const std::function<py::object(py::object)>& f, py::object arg) { if (f) { return f(arg); } return py::none();}
// 返回 std::functionstd::function<int(int)> create_multiplier(int factor) { return [factor](int x) { return x * factor; };}
std::function<py::object(py::object)> create_filter( const std::function<bool(py::object)>& predicate) { return [predicate](py::object item) -> py::object { py::gil_scoped_acquire gil; if (predicate(item)) { return item; } return py::none(); };}
PYBIND11_MODULE(stdfunction_module, m) { m.def("apply_function", &apply_function); m.def("call_function", &call_function); m.def("create_multiplier", &create_multiplier); m.def("create_filter", &create_filter, py::arg("predicate"));
// 暴露 std::function 类型本身 py::class_<std::function<int(int)>>(m, "IntToIntFunc") .def(py::init<>()) .def("__call__", &call_function);
py::class_<std::function<py::object(py::object)>>(m, "PyFunc") .def(py::init<>());}>>> import stdfunction_module as m
>>> # 传递 Python 函数到 C++>>> m.apply_function(lambda x: x + 1, 10)11
>>> # 返回 std::function 并调用>>> multiplier = m.create_multiplier(7)>>> multiplier(6)42
>>> # 过滤函数>>> is_positive = lambda x: x > 0>>> filter_fn = m.create_filter(is_positive)>>> filter_fn(5)5>>> filter_fn(-3) # 返回 None15.3 Python callable 转换
Section titled “15.3 Python callable 转换”使用 py::function 将 Python 可调用对象传入 C++。
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 接受 Python 函数作为参数py::object map_function(py::function func, const std::vector<int>& values) { py::gil_scoped_acquire gil; py::list results;
for (int v : values) { results.append(func(v)); }
return results;}
// 接受任意可调用对象py::object apply_callback(py::function callback, py::object arg) { py::gil_scoped_acquire gil; return callback(arg);}
// 组合多个函数py::function compose(py::function f, py::function g) { return py::cpp_function([f, g](py::object x) { py::gil_scoped_acquire gil; return f(g(x)); });}
// 使用 py::method_descriptor 检查 callable 属性bool is_callable(py::object obj) { return py::hasattr(obj, "__call__");}
PYBIND11_MODULE(pyfunc_module, m) { m.def("map_function", &map_function); m.def("apply_callback", &apply_callback); m.def("compose", &compose); m.def("is_callable", &is_callable);}>>> import pyfunc_module as m
>>> # 基本的 map>>> double = lambda x: x * 2>>> m.map_function(double, [1, 2, 3, 4])[2, 4, 6, 8]
>>> # 复杂转换>>> def process(x):... return x.upper() if isinstance(x, str) else x * 2
>>> m.apply_callback(process, 5)10>>> m.apply_callback(process, "hello")'HELLO'
>>> # 函数组合>>> f = lambda x: x + 1>>> g = lambda x: x * 2>>> h = m.compose(f, g) # h(x) = f(g(x))>>> h(5)11 # f(g(5)) = f(10) = 1115.4 回调函数注册
Section titled “15.4 回调函数注册”在 C++ 中注册回调,使 Python 代码可以被 C++ 调用。
#include <pybind11/pybind11.h>#include <functional>#include <vector>
namespace py = pybind11;
// 回调管理器class CallbackManager {public: // 注册回调 void register_callback(py::function cb) { py::gil_scoped_acquire gil; callbacks_.push_back(cb); }
// 触发所有回调 void trigger(const std::string& event) { py::gil_scoped_acquire gil; for (auto& cb : callbacks_) { try { cb(event); } catch (const py::error_already_set& e) { // 处理 Python 异常 std::cerr << "Callback exception: " << e.what() << std::endl; } } }
// 清除所有回调 void clear() { py::gil_scoped_acquire gil; callbacks_.clear(); }
// 返回回调数量 size_t size() const { return callbacks_.size(); }
private: std::vector<py::function> callbacks_;};
// 事件驱动的处理class EventEmitter {public: void on(const std::string& event, py::function handler) { py::gil_scoped_acquire gil; handlers_[event].push_back(handler); }
void emit(const std::string& event, py::object data) { py::gil_scoped_acquire gil; auto it = handlers_.find(event); if (it != handlers_.end()) { for (auto& handler : it->second) { handler(data); } } }
private: std::unordered_map<std::string, std::vector<py::function>> handlers_;};
PYBIND11_MODULE(callback_module, m) { py::class_<CallbackManager>(m, "CallbackManager") .def(py::init<>()) .def("register_callback", &CallbackManager::register_callback) .def("trigger", &CallbackManager::trigger) .def("clear", &CallbackManager::clear) .def("size", &CallbackManager::size);
py::class_<EventEmitter>(m, "EventEmitter") .def(py::init<>()) .def("on", &EventEmitter::on) .def("emit", &EventEmitter::emit);}>>> import callback_module as m
>>> manager = m.CallbackManager()
>>> # 注册多个回调>>> def on_event(data):... print(f"Event received: {data}")
>>> manager.register_callback(on_event)>>> manager.register_callback(lambda data: print(f"Lambda: {data}"))
>>> # 触发回调>>> manager.trigger("test_event")Event received: test_eventLambda: test_event
>>> # 事件发射器>>> emitter = m.EventEmitter()
>>> results = []>>> def handler(data):... results.append(data)
>>> emitter.on("data", handler)>>> emitter.emit("data", 42)>>> results[42]15.5 装饰器实现
Section titled “15.5 装饰器实现”利用函数式编程实现 Python 风格的装饰器。
#include <pybind11/pybind11.h>#include <functional>
namespace py = pybind11;
// 装饰器:计时装饰器py::function timer_decorator(py::function func) { return py::cpp_function([func](py::args args, py::kwargs kwargs) { auto start = std::chrono::high_resolution_clock::now();
py::object result = func(*args, **kwargs);
auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>( end - start).count();
py::gil_scoped_acquire gil; py::print("Execution time:", duration, "microseconds");
return result; });}
// 装饰器:缓存装饰器class MemoCache {public: MemoCache(py::function func) : func_(func) { py::gil_scoped_acquire gil; }
py::object get(py::object key) { auto it = cache_.find(key); if (it != cache_.end()) { return it->second; }
py::gil_scoped_acquire gil; py::object result = func_(key); cache_[key] = result; return result; }
void clear() { cache_.clear(); }
private: py::function func_; std::unordered_map<std::string, py::object> cache_; // 简化版,假设 key 可哈希};
py::function memoize(py::function func) { return py::cpp_function([func](py::object arg) -> py::object { // 简单的字符串键缓存实现 static MemoCache cache(func); return cache.get(arg); });}
// 装饰器:日志装饰器py::function log_decorator(py::function func) { return py::cpp_function([func](py::args args, py::kwargs kwargs) { py::gil_scoped_acquire gil; py::print("Calling:", func.attr("__name__"), "with", len(args), "args");
py::object result = func(*args, **kwargs);
py::print("Returned:", result); return result; });}
PYBIND11_MODULE(decorator_module, m) { m.def("timer", &timer_decorator, py::arg("func")); m.def("memoize", &memoize, py::arg("func")); m.def("log", &log_decorator, py::arg("func"));}>>> import decorator_module as m
>>> # 使用计时装饰器>>> @m.timer... def slow_function():... import time... time.sleep(0.1)... return "done"
>>> slow_function()Execution time: 101234 microseconds'done'
>>> # 使用日志装饰器>>> @m.log... def add(a, b):... return a + b
>>> add(3, 4)Calling: add with 2 argsReturned: 77
>>> # memoize 示例(简单的字符串缓存)>>> @m.memoize... def expensive_computation(x):... return x ** 2
>>> expensive_computation(5)25>>> expensive_computation(5) # 返回缓存结果25关键洞察:通过
py::cpp_function将 C++ lambda 转换为 Python callable,可以实现任意装饰器逻辑。装饰器返回一个新的 callable,拦截对原函数的调用。需要注意 GIL 管理——在持有 GIL 的情况下才能访问 Python 对象。
函数式编程总结:
| C++ 特性 | Python 对应 | 使用场景 |
|---|---|---|
lambda 表达式 | lambda | 简单内联函数 |
std::function | callable | 存储任意可调用对象 |
py::function | types.FunctionType | 接受 Python 函数 |
py::cpp_function | 无直接对应 | 将 C++ 函数暴露为 Python callable |