假设我有一个Rust结构体
struct X{...}
struct Y{
x:X
}
我希望能够编写通过Y
访问X
的python代码
y = Y()
y.x.some_method()
在PyO3中实现它的最佳方式是什么?
# [pyclass]
struct XWrapper{
x:X
}
# [pyclass]
struct YWrapper{
y:Y
}
# [pymethods]
impl YWrapper{
#[getter]
pub fn x(&self)->XWrapper{
XWrapper{x:self.y.clone()}
}
}
当然,我知道如果X
是pyclass
,那么我可以很容易地将PyRef
返回给它。但问题是,X
和Y
来自Rust库,我不可能无凭无据地将#[pyclass]
添加到它们。
2条答案
按热度按时间8dtrkrch1#
我不认为你所说的是可能的没有一些重新调整的接口:
你的
XWrapper
* 拥有 *x
,你的Y
也拥有它的x
。这意味着创建一个XWrapper
将总是涉及一个克隆(或new
)。我们可以修改
XWrapper
,使其只包含对x
的引用吗?不太可能,因为这需要给XWrapper
一个生存期注解,而PyO 3 afaik不允许pyclasses带有生存期注解。这是有意义的,因为将一个对象传递给python会将其放在python堆中,此时rust将失去对该对象的控制。那我们能做些什么?
一些想法:你 * 真的 * 需要把
y
的组成结构公开给python模块吗?仅仅因为Rust中的组织方式并不意味着Python中的组织方式也是如此。你的YWrapper
可以提供方法给python接口,在后台把请求转发给x
示例:这也将是一个受欢迎的景象严格遵守得墨忒耳的法律;)
我正在尝试其他更聪明的方法,根据
y
本身的方法如何访问和修改y.x
的一些细节,可能会向YWrapper
添加一个字段x: XWrapper
。(包括y.x
的克隆)一旦当创建YWrapper
时,并且从那时起,您可以在pub fn x
中返回对该XWrapper
的引用。当然,当x
通过y
的方法频繁地更改和更新时,这会变得更加麻烦...在某种程度上,这表明了Python的引用计数对象模型和Rust的所有权对象模型之间的冲突。Rust强制你不能随意地弄乱对象,除非你是它们的所有者。
2hh7jdfx2#
共享对象并返回它们或改变它们确实是可能的。无论如何,就像Python一样。Lagerbaer的建议是可行的,它实际上非常适合小代码。然而,如果方法的数量增加,将需要大量的重复和样板(更糟糕的是,每次增加嵌套的深度时都会折叠)。
我不知道这是否是我们应该做的事情。但据我所知,做这件事的方法是使用
Py
。上帝希望我有一个习惯,在实验前彻底阅读文档。在https://docs.rs/pyo3/latest/pyo3/#the-gil-independent-types的MAIN PAGE中的doc表示:当用Py Package 时<...>,就像Py或Py一样,Python对象不再有有限的生命周期,这使得它们更容易存储在结构体中并在函数之间传递。然而,如果没有Python〈'py〉标记,你就不能用它们做很多事情,你需要重新获得GIL。
Py
是“对Python堆上分配的对象的独立于GIL的引用。”https://docs.rs/pyo3/latest/pyo3/prelude/struct.Py.html换句话说,要返回
pyclass
对象,我们需要像Py<pyclass_struct_name>
那样 Package 它。你的例子太复杂了,老实说我不明白你想做什么,但这里有一个更适合我自己的例子的替代版本。因为这基本上是谷歌弹出的唯一结果之一,我认为它适合粘贴在这里,即使它不是一个确切的回应提供了上述的例子。
所以我们开始吧...
假设我们有一个Rust结构体
X
,我们不能像你提到的那样修改库,我们需要一个XWrapper
(我们称之为PyX
)pyclass
来保存它。因此,我们在此定义它们:
第一个
然后对于用法,我们所要做的就是用GIL锁初始化对象(假设在XWrapper的init中),然后为它定义一个getter。* 这里的重要注意事项是,您在它上调用
clone_ref
,并且不要返回该对象 *。这基本上是一个嵌套的类系统,嵌套的对象是不可变的(具有内部可变性),所以这也是嵌套代码的一种奇妙的方式。
在下面的示例中,我将所需的
X
用作另一个名为Api
的 Package 器中的PyX
。