Skip to content

Commit

Permalink
Merge #3067
Browse files Browse the repository at this point in the history
3067: docs: show pattern for self.__class__.__name__ in __repr__ r=adamreichold a=wjones127

It took me a little while to figure out this pattern in PyO3, so I thought it would be a good addition to the guide.

It's not the cleanest pattern, so would welcome suggestions on how to make it shorter or easier.

Co-authored-by: Will Jones <willjones127@gmail.com>
  • Loading branch information
bors[bot] and wjones127 committed Mar 30, 2023
2 parents 90d50da + 52a0378 commit 77fc6e6
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 5 deletions.
6 changes: 4 additions & 2 deletions guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,10 @@ impl Number {
Self(value)
}

fn __repr__(&self) -> String {
format!("Number({})", self.0)
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
// Get the class name dynamically in case `Number` is subclassed
let class_name: &str = slf.get_type().name()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}

fn __str__(&self) -> String {
Expand Down
31 changes: 28 additions & 3 deletions guide/src/class/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ impl Number {
// 👇 Tuple field access in Rust uses a dot
format!("Number({})", self.0)
}

// `__str__` is generally used to create an "informal" representation, so we
// just forward to `i32`'s `ToString` trait implementation to print a bare number.
fn __str__(&self) -> String {
Expand All @@ -70,6 +69,31 @@ impl Number {
}
```

#### Accessing the class name

In the `__repr__`, we used a hard-coded class name. This is sometimes not ideal,
because if the class is subclassed in Python, we would like the repr to reflect
the subclass name. This is typically done in Python code by accessing
`self.__class__.__name__`. In order to be able to access the Python type information
*and* the Rust struct, we need to use a `PyCell` as the `self` argument.

```rust
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
// This is the equivalent of `self.__class__.__name__` in Python.
let class_name: &str = slf.get_type().name()?;
// To access fields of the Rust struct, we need to borrow the `PyCell`.
Ok(format!("{}({})", class_name, slf.borrow().0))
}
}
```

### Hashing


Expand Down Expand Up @@ -235,8 +259,9 @@ impl Number {
Self(value)
}

fn __repr__(&self) -> String {
format!("Number({})", self.0)
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
let class_name: &str = slf.get_type().name()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}

fn __str__(&self) -> String {
Expand Down

0 comments on commit 77fc6e6

Please sign in to comment.