Classes
We've covered Python functions written in Rust, but what about classes?
Defining a class
You can use the #[pyclass]
attribute to define a new Python class in Rust. Here's an example:
#![allow(unused)] fn main() { use pyo3::prelude::*; #[pyclass] struct Wallet { balance: i32, } }
It defines a new Python class called Wallet
with a single field, balance
.
Registering a class
Just like with #[pyfunction]
s, you must explicitly register your class with a module to make it visible to
users of your extension.
Continuing with the example above, you'd register the Wallet
class like this:
#![allow(unused)] fn main() { #[pymodule] fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::<Wallet>()?; Ok(()) } }
IntoPy
Rust types that have been annotated with #[pyclass]
automatically implement the IntoPy
trait, thus
allowing you to return them from your #[pyfunction]
s.
For example, you can define a function that creates a new Wallet
instance:
#![allow(unused)] fn main() { #[pyfunction] fn new_wallet(balance: i32) -> Wallet { Wallet { balance } } }
It'll compile just fine, handing over a new Wallet
instance to the Python caller.
Attributes
By default, the fields of your #[pyclass]
-annotated structs aren't accessible to Python callers.
Going back to our Wallet
example—if you try to access the balance
field from Python, you'll get an error:
wallet = new_wallet(0)
> assert wallet.balance == 0
E AttributeError: 'builtins.Wallet' object has no attribute 'balance'
tests/test_sample.py:8: AttributeError
The same error would occur even if you made balance
a public field.
To make the field accessible to Python, you must add a getter.
This can be done using the #[pyo3(get)]
attribute:
#![allow(unused)] fn main() { #[pyclass] struct Wallet { #[pyo3(get)] balance: i32, } }
Now, the balance
field is accessible from Python:
def test_wallet():
wallet = new_wallet(0)
assert wallet.balance == 0
If you want to allow Python callers to modify the field, you can add a setter using the #[pyo3(set)]
attribute:
#![allow(unused)] fn main() { #[pyclass] struct Wallet { // Both getter and setter #[pyo3(get, set)] balance: i32, } }
Exercise
The exercise for this section is located in 02_classes/00_pyclass