Type Conversions
PyO3 provides some handy traits to convert between Python types and Rust types.
.extract() and the FromPyObject trait
The easiest way to convert a Python object to a Rust value is using
.extract(). It returns a PyResult with a type error if the conversion
fails, so usually you will use something like
let v: Vec<i32> = obj.extract()?;
This method is available for many Python object types, and can produce a wide
variety of Rust types, which you can check out in the implementor list of
FromPyObject.
FromPyObject is also implemented for your own Rust types wrapped as Python
objects (see the chapter about classes). There, in order to both be
able to operate on mutable references and satisfy Rust's rules of non-aliasing
mutable references, you have to extract the PyO3 reference wrappers PyRef
and PyRefMut. They work like the reference wrappers of
std::cell::RefCell and ensure (at runtime) that Rust borrows are allowed.
The ToPyObject trait
ToPyObject is a conversion trait that allows various objects to be
converted into PyObject. IntoPy<PyObject> serves the
same purpose, except that it consumes self.
*args and **kwargs for Python object calls
There are several ways how to pass positional and keyword arguments to a Python object call.
The ObjectProtocol trait provides two methods:
call- call any callable Python object.call_method- call a specific method on the object, shorthand forget_attrthencall.
Both methods need args and kwargs arguments, but there are variants for less
complex calls, such as call1 for only args and call0 for no arguments at all.
use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; struct SomeObject; impl SomeObject { fn new(py: Python) -> PyObject { PyDict::new(py).to_object(py) } } fn main() { let arg1 = "arg1"; let arg2 = "arg2"; let arg3 = "arg3"; let gil = Python::acquire_gil(); let py = gil.python(); let obj = SomeObject::new(py); // call object without empty arguments obj.call0(py); // call object with PyTuple let args = PyTuple::new(py, &[arg1, arg2, arg3]); obj.call1(py, args); // pass arguments as rust tuple let args = (arg1, arg2, arg3); obj.call1(py, args); }
kwargs can be None or Some(&PyDict). You can use the
IntoPyDict trait to convert other dict-like containers,
e.g. HashMap or BTreeMap, as well as tuples with up to 10 elements and
Vecs where each element is a two-element tuple.
use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyDict}; use std::collections::HashMap; struct SomeObject; impl SomeObject { fn new(py: Python) -> PyObject { PyDict::new(py).to_object(py) } } fn main() { let key1 = "key1"; let val1 = 1; let key2 = "key2"; let val2 = 2; let gil = Python::acquire_gil(); let py = gil.python(); let obj = SomeObject::new(py); // call object with PyDict let kwargs = [(key1, val1)].into_py_dict(py); obj.call(py, (), Some(kwargs)); // pass arguments as Vec let kwargs = vec![(key1, val1), (key2, val2)]; obj.call(py, (), Some(kwargs.into_py_dict(py))); // pass arguments as HashMap let mut kwargs = HashMap::<&str, i32>::new(); kwargs.insert(key1, 1); obj.call(py, (), Some(kwargs.into_py_dict(py))); }
FromPy<T> and IntoPy<T>
Many conversions in PyO3 can't use std::convert::From because they need a GIL token.
The FromPy trait offers an from_py method that works just like from, except for taking a Python<'_> argument.
I.e. FromPy<T> could be converting a Rust object into a Python object even though it is called FromPy - it doesn't say anything about which side of the conversion is a Python object.
Just like From<T>, if you implement FromPy<T> you gain a blanket implementation of IntoPy for free.
Eventually, traits such as ToPyObject will be replaced by this trait and a FromPy trait will be added that will implement
IntoPy, just like with From and Into.