问题描述
有效但不是我想要的最小示例:
use std::collections::HashMap;
use pyo3::class::basic::CompareOp;
use pyo3::class::PyObjectProtocol;
use pyo3::prelude::*;
#[pyclass]
struct MyStruct {
foo: HashMap<i32,Vec<i32>>,}
#[pyproto]
impl PyObjectProtocol for MyStruct {
fn __richcmp__(&self,other: PyRef<MyStruct>,op: CompareOp) -> Py<PyAny> {
let gil = Python::acquire_gil();
let py = gil.python();
if let CompareOp::Eq = op {
let other_foo = &(other.foo);
let res = self.foo == *other_foo;
return res.into_py(py);
}
py.NotImplemented();
}
}
因此,这将引用位于 python 堆中的 MyStruct
并对其运行比较,我的理解是不会发生复制/克隆。
但这不是pythonic的方式。参数 other
不必是同一类型。比较方法应该只返回 TypeError
,而不是引发 NotImplemented
。
我正在尝试更改签名,以便
- 我可以接受任意的 Python 对象。我以为
&PyAny
会恰到好处 - 然后我可以检查类型,或尝试转换,但无需将对象从 Python 堆克隆到 Rust 的堆。
有什么方法可以将 &PyAny
转换为 PyRef<MyStruct>
data %>%
group_by(ID) %>%
summarize(count = n_distinct(Var)) %>%
mutate(count = if_else(ID == 3L,sum(count) - count,count))
# # A tibble: 3 x 2
# ID count
# <int> <int>
# 1 1 3
# 2 2 3
# 3 3 6
?
解决方法
嗯,原来答案很简单。我无法让它工作的原因是 PyO3 发生了变化。
过去,当您拥有 pyclass FromPyObject
时,特征 &T
会自动为 T
实现。
但现在不是这样了。
正确的做法是:
fn do_stuff(any: &PyAny) {
let res: PyResult<PyRef<MyStruct>> = any.extract();
if res.is_err() { // whoops; wasn't a MyStruct after all }
}
来源:https://pyo3.rs/v0.13.2/migration.html
对象提取
对于 PyClass 类型 T
,&T
和 &mut T
不再具有 FromPyObject
实现。相反,您应该分别提取 PyRef<T>
或 PyRefMut<T>
。如果 T
实现 Clone
,您可以提取 T
本身。此外,您还可以提取 &PyCell<T>
,尽管您很少需要它。