You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pybind11绑定类方法返回新实例而非原地修改问题

解决pybind11中类方法返回原输入实例的问题

问题背景

在pybind11绑定C++代码时,ObstacleProcess类的__call__方法处理Obstacle实例列表后,返回的是新创建的Obstacle实例而非原输入实例,导致Python中对象地址发生变化,不符合预期。

Python复现代码

import example

obstacle_1 = example.Obstacle()
obstacle_2 = example.Obstacle()

obstacles = [obstacle_1, obstacle_2]
print(obstacles)

params = example.Params()
obstacle_process = example.ObstacleProcess(params)
obstacles = obstacle_process(obstacles)
print(obstacles)

输出现象

第一次打印输出:

[<example.Obstacle object at 0x7fb65271e1b0>, <example.Obstacle at 0x7fb652735070>]

第二次打印输出:

[<example.Obstacle at 0x7fb652734670>, <example.Obstacle object at 0x7fb652735230>]

原obstacle_1的地址从0x7fb65271e1b0变为0x7fb652734670,说明返回的是新实例而非原输入实例。

相关C++代码

pybind11绑定代码

// pybind11 binding
py::class_<Obstacle, std::shared_ptr<Obstacle>>(m, "Obstacle")
    .def(py::init<>());

py::class_<ObstacleProcess>(m, "ObstacleProcess")
    .def(py::init<const Params&>())
    .def("__call__", &ObstacleProcess::operator_py<Params>, py::return_value_policy::reference);

operator_py实现

template <Params>
std::vector<Obstacle>& operator_py(
    std::vector<Obstacle>& obstacles,
    const Params &parameters
)
{
    // 处理逻辑
    ...

    return obstacles;
}

已尝试方案

  • 尝试使用/不使用std::shared_ptr<Obstacle>,结果一致,说明shared_ptr绑定方式存在问题
  • 尝试PYBIND11_MAKE_OPAQUE(std::shared_ptr<std::vector<Obstacle>>);,无效果
  • 尚未尝试pybind11的smart_holder分支

解决方案

核心问题分析

当前代码中std::vector<Obstacle>存储的是Obstacle对象的值拷贝,而非指针或智能指针。当Python传入列表时,pybind11会将Python列表转换为std::vector<Obstacle>,这个过程会创建新的Obstacle实例(值拷贝),而非引用原实例。后续处理返回的是拷贝后的vector,因此Python中看到的是新对象。

修正步骤

  1. 修改C++数据结构
    将std::vector<Obstacle>改为std::vector<std::shared_ptr<Obstacle>>,确保存储对象引用而非值:
template <Params>
std::vector<std::shared_ptr<Obstacle>>& operator_py(
    std::vector<std::shared_ptr<Obstacle>>& obstacles,
    const Params &parameters
)
{
    // 处理逻辑:直接操作shared_ptr指向的原对象
    ...

    return obstacles;
}
  1. 调整pybind11绑定
  • 为std::vector<std::shared_ptr<Obstacle>>添加绑定:
    py::bind_vector<std::vector<std::shared_ptr<Obstacle>>>(m, "ObstacleList");
    
  • 修改__call__绑定,确保返回值策略正确并维持对象生命周期:
    .def("__call__", &ObstacleProcess::operator_py<Params>, 
         py::return_value_policy::reference,
         py::keep_alive<1, 2>()); // 保证输入的vector在方法调用期间存活
    
  1. Python端适配
    无需修改Python代码,pybind11会自动将Python的Obstacle实例列表转换为std::vector<std::shared_ptr<Obstacle>>。

关于smart_holder分支

不需要使用该分支,当前pybind11主分支已支持std::shared_ptr的正确引用传递,问题核心在于数据结构设计,而非绑定方式局限。

内容的提问来源于stack exchange,提问作者JMy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:35:40