Skip to content

第15章 函数式编程支持

pybind11 提供了丰富的函数式编程支持,让 C++ 的 lambda、函数对象和 std::function 可以无缝转换为 Python 可调用对象。

C++ lambda 表达式可以直接暴露给 Python 作为可调用对象。

#include <pybind11/pybind11.h>
namespace py = pybind11;
// 捕获空白的 lambda
auto square = [](int x) { return x * x; };
// 捕获单个值的 lambda
auto 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 对象的情况下。

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::function
std::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) # 返回 None

使用 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) = 11

在 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_event
Lambda: test_event
>>> # 事件发射器
>>> emitter = m.EventEmitter()
>>> results = []
>>> def handler(data):
... results.append(data)
>>> emitter.on("data", handler)
>>> emitter.emit("data", 42)
>>> results
[42]

利用函数式编程实现 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 args
Returned: 7
7
>>> # 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::functioncallable存储任意可调用对象
py::functiontypes.FunctionType接受 Python 函数
py::cpp_function无直接对应将 C++ 函数暴露为 Python callable