嵌套一个#[pyclass]在另一个#[pyclass]中。

huangapple go评论62阅读模式
英文:

Embedded a #[pyclass] in another #[pyclass]

问题

#[pymethods]
impl ClassA {
    fn ret_class_b(&self, field_b: &PyAny) -> PyResult<ClassB> {
        let class_a: Py<PyAny> = Py::clone_ref(self, field_b.py());
        Ok(ClassB {
            class_a: Py::new(field_b.py(), ClassA {
                pv: Some(class_a),
                field_a: field_b.into_py()?,
            })?,
            field_b: field_b.into_py(),
        })
    }
}
英文:

I am trying to implement a cache for the private variable of any python class.

Let's suppose I have this:

#[pyclass]
struct ClassA {
    pv: Py&lt;PyAny&gt;, // GIL independent type, storable in another #[pyclass]
    field_a: Py&lt;PyAny&gt;,
}

#[pyclass]
struct ClassB {
    class_a: Py&lt;ClassA&gt;,
    field_b: Py&lt;PyAny&gt;,
}

In the impls of ClassA, I have a method attempting to 'put' a reference of the ClassA object into the ClassB's class_a field. And return a newly instantiated ClassB object.

#[pymethods]
impl ClassA {
    fn ret_class_b {&amp;self, field_b: &amp;PyAny) -&gt; PyResult&lt;ClassB&gt; {
        let class_a: Py&lt;ClassA&gt; = Py::clone_ref(self, field_b.py());
        ClassB {
            class_a: class_a,
            field_b: field_b.into_py(),
        }
    }
}

The above does not compile.

The issue is how do I get &amp;Py&lt;ClassA&gt; from the receiver of the method so as to return another object where the receiver is referred to as a field in that object?

Edit / Update

Thanks to @cafce25 for his reminder on giving fully reproducible codes and the error from the compiler.

Here is it:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

use pyo3::{
    prelude::{*, Py, PyAny},
};

#[pymodule]
fn stackoverflowqn(py: Python, pymod: &amp;PyModule) -&gt; PyResult&lt;()&gt; {
    #[pyclass(name = &quot;class_a&quot;)]
    #[derive(Clone, Debug)]
    pub struct ClassA {
        pv: Option&lt;Py&lt;PyAny&gt;&gt;, // private value
        field_a: Py&lt;PyAny&gt;,
    }

    #[pyclass]
    #[derive(Clone, Debug)]

    pub struct ClassB {
        class_a: Py&lt;ClassA&gt;,
        field_b: Py&lt;PyAny&gt;,
    }

    #[pymethods]
    impl ClassA {
        #[new]
        pub fn __new__(_slf: PyRef&lt;&#39;_, Self&gt;, field_a: PyObject) -&gt; PyResult&lt;ClassA&gt; {
            Ok(ClassA {
                pv: None,
                field_a: field_a,
            })
        }

        fn ret_class_b {&amp;self, field_b: &amp;PyAny) -&gt; PyResult&lt;ClassB&gt; {
            let class_a: Py&lt;ClassA&gt; = Py::clone_ref(self, field_b.py());
            Ok(ClassB {
                class_a: class_a,
                field_b: field_b.into_py(),
            })
        }
    }
}

<!-- end snippet -->

Here is the compiler error:

error[E0277]: the trait bound `ClassA: pyo3::IntoPy&lt;pyo3::Py&lt;ClassA&gt;&gt;` is not satisfied
  --&gt; crates\cached-property\src\stackoverflow.rs:36:53
   |
36 |                 pyo3::IntoPy::&lt;Py&lt;ClassA&gt;&gt;::into_py(pty_getter, py);
   |                 ----------------------------------- ^^^^^^^^^^ the trait `pyo3::IntoPy&lt;pyo3::Py&lt;ClassA&gt;&gt;` is not implemented for `ClassA`
   |                 |
   |                 required by a bound introduced by this call
   |
   = help: the trait `pyo3::IntoPy&lt;pyo3::Py&lt;PyAny&gt;&gt;` is implemented for `ClassA`

答案1

得分: 1

以下是翻译好的部分:

问题是如何从方法的接收者中获取 &amp;Py&lt;ClassA&gt;,以便返回另一个对象,其中接收者被称为该对象中的一个字段?

如果我理解正确,您的问题是如何从您的方法中通过 &amp;self 构造 Py&lt;ClassA&gt;,而 &amp;ClassA 可用于该方法。我认为您正在寻找的是更改方法签名。用户指南中有很少关于这方面的文档,但如果您查看 PyRef 中的示例,您会看到您可以将 PyRef&lt;&#39;_, Self&gt; 的实例传递给您的方法,而不是 &amp;self

您可以使用 PyRef 作为 &amp;self 接收者的替代方式。

您可以通过调用 .into()PyRef 转换为 Py 指针。

我对您的示例进行了一些更改,这在我的计算机上可以编译:

#[pyclass]
struct ClassA {
    pv: Py&lt;PyAny&gt;, // GIL 独立类型,可存储在另一个 #[pyclass] 中
    field_a: Py&lt;PyAny&gt;,
}

#[pymethods]
impl ClassA {
    fn ret_class_b(slf: PyRef&lt;&#39;_, Self&gt;, field_b: Py&lt;PyAny&gt;) -&gt; PyResult&lt;ClassB&gt; {
        Ok(ClassB {
            class_a: slf.into(),
            field_b,
        })
    }
}

#[pyclass]
struct ClassB {
    class_a: Py&lt;ClassA&gt;,
    field_b: Py&lt;PyAny&gt;,
}

希望这对您有所帮助。

英文:

> The issue is how do I get &amp;Py&lt;ClassA&gt; from the receiver of the method so as to return another object where the receiver is referred to as a field in that object?

If I understood correctly your problem is constructing Py&lt;ClassA&gt; from &amp;ClassA available in your method through &amp;self. I think what you are looking for is a change in your method signature. There is little documentation on this in the user guide, but if you look at the examples in PyRef you see that you can pass an instance of PyRef&lt;&#39;_, Self&gt; into your method instead of &amp;self:

> You can use PyRef as an alternative to a &amp;self receiver

You can convert PyRef into a Py pointer simply by calling .into().

I've made some changes to your example and this compiles on my computer:

#[pyclass]
struct ClassA {
    pv: Py&lt;PyAny&gt;, // GIL independent type, storable in another #[pyclass]
    field_a: Py&lt;PyAny&gt;,
}

#[pymethods]
impl ClassA {
    fn ret_class_b(slf: PyRef&lt;&#39;_, Self&gt;, field_b: Py&lt;PyAny&gt;) -&gt; PyResult&lt;ClassB&gt; {
        Ok(ClassB {
            class_a: slf.into(),
            field_b,
        })
    }
}

#[pyclass]
struct ClassB {
    class_a: Py&lt;ClassA&gt;,
    field_b: Py&lt;PyAny&gt;,
}

huangapple
  • 本文由 发表于 2023年3月3日 18:14:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75625753.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定