复制 boost.python 对象

Posted

技术标签:

【中文标题】复制 boost.python 对象【英文标题】:Copying a boost.python object 【发布时间】:2011-01-14 23:19:00 【问题描述】:

我有一些 boost python 类,我在 python 中实例化了它们。我想复制它们。所以,如果我有

p = Bernoulli(0.5)

我想做

q = Bernoulli(p)

但是如果我不知道 p 的类型怎么办?我试着这样做:

q = copy.deepcopy(p)

但是python说它不能腌制p。

在 Bernoulli 的接口中添加 clone() 函数是我唯一的解决方案吗?或者我可以以某种方式自动生成该方法吗?可以使 copy.deepcopy 与 Boost.python 对象一起使用吗?

【问题讨论】:

【参考方案1】:

来自http://mail.python.org/pipermail/cplusplus-sig/2009-May/014505.html

#define PYTHON_ERROR(TYPE, REASON) \
 \
    PyErr_SetString(TYPE, REASON); \
    throw bp::error_already_set(); \


template<class T>
inline PyObject * managingPyObject(T *p)

    return typename bp::manage_new_object::apply<T *>::type()(p);


template<class Copyable>
bp::object
generic__copy__(bp::object copyable)

    Copyable *newCopyable(new Copyable(bp::extract<const Copyable
&>(copyable)));
    bp::object
result(bp::detail::new_reference(managingPyObject(newCopyable)));

    bp::extract<bp::dict>(result.attr("__dict__"))().update(
        copyable.attr("__dict__"));

    return result;


template<class Copyable>
bp::object
generic__deepcopy__(bp::object copyable, bp::dict memo)

    bp::object copyMod = bp::import("copy");
    bp::object deepcopy = copyMod.attr("deepcopy");

    Copyable *newCopyable(new Copyable(bp::extract<const Copyable
&>(copyable)));
    bp::object
result(bp::detail::new_reference(managingPyObject(newCopyable)));

    // HACK: copyableId shall be the same as the result of id(copyable)
in Python -
    // please tell me that there is a better way! (and which ;-p)
    int copyableId = (int)(copyable.ptr());
    memo[copyableId] = result;

    bp::extract<bp::dict>(result.attr("__dict__"))().update(
        deepcopy(bp::extract<bp::dict>(copyable.attr("__dict__"))(),
memo));

    return result;

使用它:

class_<foo>(foo)
   .def("__copy__", &generic__copy__< foo >)
   .def("__deepcopy__", &generic__deepcopy__< foo >)
   .def(init< const foo & >())

【讨论】:

我会自己发布你知道的。现在我只能投票给你。 GRRRRR! @Matthew,哈哈哈。也许你会好心地向我解释一下我应该在 64 位编译器上做什么,因为指针不能转换为 int? @Matthew,另外,我不确定为什么这个 generic__copy__ 比 Philipp 的更好?是因为它复制了您可能添加到对象字典中的任何内容吗? (我最终通过 long long 转换为 int,因为我注意到 python id 似乎是 32 位整数。) @Neil G,我不确定您应该如何处理 64 位情况。现在,我们的包装库只存在于 32 位,所以我们没有这个问题。真正的解决方案是让 C/Python API 或 BP 公开一种可移植的方式来获取 id(copyable) 作为 int。【参考方案2】:

对于复制,您可以实现__copy____deepcopy__ 特殊方法(其中一个可以只包装复制构造函数,具体取决于类的C++ 复制语义),或者添加pickling support。 copy 模块将使用特殊的复制方法(如果可用),否则使用酸洗方法。

这里是一个使用拷贝构造函数实现__copy__的例子:

template<typename T> const T copyObject(const T& v)  return v; 
boost::python::class_<C>("C").def("__copy__", copyObject<C>);

【讨论】:

我如何使用 boost.python 在复制构造函数方面实现__copy__ 非常感谢,但我收到错误消息:Boost.Python.ArgumentError: Python argument types in Dirichlet.__deepcopy__(Dirichlet, dict) did not match C++ signature: __deepcopy__(Dirichlet) @Neil:__deepcopy__ 方法有一个不同的签名:它传递了一个用于记忆的字典。【参考方案3】:

是的,您可以通过在您的对象上实现 __setstate____getstate__ 方法来使 boost::python 对象可深度复制(也可以选择)。

基本上,__getstate__ 应该返回一个代表对象内部状态的 (python) 对象,而 __setstate__ 显然接受所述对象并更新对象的状态。

如果您的对象接受__init__ 的参数,您还应该考虑实现__getinitargs__

有关更多信息,请参阅 Python docs。

【讨论】:

使 BP 对象可腌制是迄今为止使其可复制的最糟糕的方法。 C++ 有很好的高效复制 ctors。酸洗需要通过一根绳子来回往返,相比之下,它的成本太高了。这甚至不是使 BP 对象可腌制的最佳方法。参见:boost::python::pickle_suite

以上是关于复制 boost.python 对象的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Python 返回一个不可复制对象的列表

复制 boost.python 对象

如何从 boost::python 返回 numpy.array?

如何使用“=”操作符复制 boost python 列表或他的引用

std::vector 到 boost::python::list 而不复制数据

Boost.Python 对象的 id