将 PyAny 转换为给定类型的 PyRef,而无需额外的提取成本?

问题描述

有效但不是我想要的最小示例:

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>,尽管您很少需要它。