【问题标题】:How to copy boost python list or his reference with "=" operator如何使用“=”操作符复制 boost python 列表或他的引用
【发布时间】:2013-12-23 03:54:16
【问题描述】:

获取此代码

#include<boost/python>
namespace bp = boost::python;

bp::list py_points; //initial list
other_class* C; // this class have a bp::list attribute called py_list

// ... some code ....

// in this part C.py_list.ptr() is 0x0

other_class->py_list = py_list; // problem here!!

问题在于运算符“=”

在调试器中的object_core.hpp文件中,这是一个boost python核心文件

inline api::object_base& api::object_base::operator=(api::object_base const& rhs)
{
    Py_INCREF(rhs.m_ptr);
    Py_DECREF(this->m_ptr); // in this line the program fail
    this->m_ptr = rhs.m_ptr;
    return *this;
}

使用运算符“=”的正确方法是什么

已编辑

问题出在堆栈上,如果指针 other_class->py_list 为 null(或 None,因为未调用类构造函数)程序无法调用函数 Py_DECREF(不存在NULL 指针之前的引用)

问题是修复调用构造函数

other_class* C = new othe_class(); // fixed!!

【问题讨论】:

    标签: c++ python boost-python python-extensions


    【解决方案1】:

    问题不在于赋值运算符,问题在于py_list的内部PyObject指针是nullptr。在大多数情况下,指针不应为空。从 Python 的角度来看,它至少应该管理对 Python None 对象的引用,就像它由默认构造的 boost::python::object 完成的那样。 boost::python::list 的默认构造函数创建一个新的空列表。因此,问题的根源可能在于 other_class 的构造函数或 “some code” 块。


    为了详细说明标题中提出的问题,在 Boost.Python 中创建引用或复制列表与在 Python 中相同:

    • 赋值运算符将创建对列表的引用。

      >>> a = ['spam']
      >>> b = a
      >>> b
      ['spam']
      >>> a is b
      True
      
    • 可以slice一个列表来创建一个浅拷贝。

      >>> a = ['spam']
      >>> b = a[:]
      >>> b
      ['spam']
      >>> a is b
      False
      

    这是一个完整的示例,其中包含在 cmets 中注释的 Python 等效代码。

    #include <iostream>
    #include <boost/python.hpp>
    
    /// @brief Mockup class.
    struct other_class
    {
      boost::python::list py_list;
    };
    
    /// @brief Helper function to print object id and its string representation.
    std::string to_string(boost::python::object& o)
    {
      std::stringstream stream;
      stream << o.ptr() << " = "
             << boost::python::extract<std::string>(o.attr("__str__")())();
      return stream.str();
    }
    
    int main()
    {
      using std::cout;
      using std::endl;
      namespace python = boost::python;
    
      Py_Initialize();
      try
      {
        python::object object;                            // object = None
        cout << to_string(object) << "\n"                 // print object
             << "  is none check: " << object.is_none()   // print object is None
             << endl;
    
        // Create other_class and populate its list.
        other_class* c = new other_class();               // py_list = []
        cout << "c->py_list: " << to_string(c->py_list)   // print py_list
             << endl;
        c->py_list.append("spam");                        // py_list.append("spam")
        cout << "c->py_list: " << to_string(c->py_list)   // print py_list
             << endl;
    
        // Have list1 reference c->py_list.
        python::list list1;                                // list1 = []
        cout << "list1: " << to_string(list1) << "\n"      // print list1
             << "assign py_list to list1" << endl;
        list1 = c->py_list;                                // list1 = py_list
        cout << "list1: " << to_string(list1) << endl;     // print list1
    
        // Modify list1 and observe effects on pylist.
        cout << "modify list1" << endl;
        list1.append(42);                                 // list1.append(42)
        cout << "c->py_list: " << to_string(c->py_list)   // print py_list
             << endl;
    
        // Shallow-copy list1.
        cout << "copying list1 into list2" << endl;
        python::list list2(
            list1.slice(python::_, python::_));            // list2 = list1[:]
        list2.append("eggs");                              // list2.append("eggs")
        cout << "list2: " << to_string(list2) << "\n"      // print list2
             << "list1: " << to_string(list1) << endl;     // print list1
    
        delete c;
      }
      catch (python::error_already_set&)
      {
        PyErr_Print();
      }
    }
    

    输出:

    0x804e1ac = None
      is none check: 1
    c->py_list: 0xb707024c = []
    c->py_list: 0xb707024c = ['spam']
    list1: 0xb70da98c = []
    assign py_list to list1
    list1: 0xb707024c = ['spam']
    modify list1
    c->py_list: 0xb707024c = ['spam', 42]
    copying list1 into list2
    list2: 0xb707cb0c = ['spam', 42, 'eggs']
    list1: 0xb707024c = ['spam', 42]
    

    输出中需要注意的几点:

    • 默认构造的boost::python::list 对象管理对空列表的引用。 0x804e1acNone,并且列表对象的内部 PyObject 指针都不管理对它的引用。
    • list1 = py_list 分配导致list1 管理对由py_list 管理的同一列表的引用。这在list1 的输出中显示,最初管理对0xb70da98c 的引用,但在分配后,它管理对0xb707024c 的引用。使用list1py_list 管理同一个列表,通过一个句柄对列表的更改可以在另一个句柄中观察到。
    • 切片构建了一个新列表。因此,list2PyObject 内部点管理的引用 (0xb707cb0c) 与 list1 的指针 (0xb707024c) 不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-09
      • 1970-01-01
      • 2016-01-19
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多