PyO3-将扩展另一个类的类作为函数参数传递



我在Rust中有一个库,我希望能够从Python中使用它。所以我可以使用PyO3进行绑定。

假设我在Rust中有以下库:

pub trait Animal {
fn make_sound();
}
pub struct Dog {}
impl Animal for Dog {
fn make_sound() {
println!("whoof whoof");
}
}
pub struct Cat {}
impl Animal for Cat {
fn make_sound() {
println!("miaauwwww");
}
}
pub fn make_three_sounds(&impl Animal) {
animal.make_sound();
animal.make_sound();
animal.make_sound();
}

该库的用户可以使用结构Dog和Cat以及函数make_three_sounds。但他们也可以使用特性Animal来定义自己的动物,然后将其传递给函数make_three_sounds。

现在,让我们假设我希望从Python中使用这个库。所以我可以用PyO3制作Rust->Python绑定。然而,问题是,在Python中,这将以不同的方式实现。你将拥有一个可以扩展的类,而不是特质。所以在Python中,这个库看起来是这样的:

Class Animal:
def make_sound():
raise Error("Unimplemented abstract method")
class Dog(Animal):
def make_sound():
print("woaggh woafgg")
class Cat(Animal):
def make_sound():
print("miaaoww")
def make_three_sounds(a: Animal):
a.make_sound()
a.make_sound()
a.make_sound()

为了制作一个类狗和猫,我只需要使用"[pyclass]";以及"#[pymethods]";在Dog和Cat结构之上。问题在于生成一个Animal类,您可以扩展该类,然后将其传递给函数make_three_sounds。为了制作Animal类,我可以创建一个使用Animal特性的结构,然后添加"#[pyclass]";除此之外。

但问题是:当Python库的用户扩展该类(Animal(,然后将其传递给make_three_sounds时,它将不起作用,因为make_three_sounds必须接受一个实现trait Animal的结构,而该Python对象不实现该trait(尽管它是扩展实现它的类/结构的类的对象(。我可以制作一个接受PyAny对象的make_three_sounds函数的包装器,但以后如何将该对象传递给实际的make_three_sounds函数?

我可以看到的一个解决方案如下。

我创建了一个函数make_three_sounds(PyAny a),它将作为Rustmake_three_sounds(&impl Animal a)函数的包装器。该包装器将接受PyAny类型的对象。稍后我将执行以下操作。我将有一个特殊的结构PyAnimal实现特性Animal。该特征将具有一个名为python_object的属性。Python中的PyAny对象(表示扩展Animal类的对象(稍后将分配给该属性python_object。PyAnimal必须实现trait方法,该方法的实现将在python_object上调用该方法(PyAny结构有一个callmethod方法,可以调用该对象的任何方法(。然后,该PyAnimal对象将被传递给make_three_sounds(&impl Animal a)函数。因此PyAnimal的实现或多或少会如下:

struct PyAnimal {
python_object: PyAny
}
impl Animal for PyAnimal {
fn make_sound() {
python_object.call_method('make_sound');
}
}

最新更新