guide: explain how #[new] works w.r.t. native base types

This commit is contained in:
Georg Brandl 2022-11-20 08:25:42 +01:00
parent 2a630a2a52
commit 51eeb6db96
1 changed files with 40 additions and 2 deletions

View File

@ -233,7 +233,7 @@ Consult the table below to determine which type your constructor should return:
## Inheritance ## Inheritance
By default, `PyAny` is used as the base class. To override this default, By default, `object`, i.e. `PyAny` is used as the base class. To override this default,
use the `extends` parameter for `pyclass` with the full path to the base class. use the `extends` parameter for `pyclass` with the full path to the base class.
For convenience, `(T, U)` implements `Into<PyClassInitializer<T>>` where `U` is the For convenience, `(T, U)` implements `Into<PyClassInitializer<T>>` where `U` is the
@ -310,7 +310,8 @@ impl SubSubClass {
``` ```
You can also inherit native types such as `PyDict`, if they implement You can also inherit native types such as `PyDict`, if they implement
[`PySizedLayout`]({{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PySizedLayout.html). However, this is not supported when building for the Python limited API (aka the `abi3` feature of PyO3). [`PySizedLayout`]({{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PySizedLayout.html).
This is not supported when building for the Python limited API (aka the `abi3` feature of PyO3).
However, because of some technical problems, we don't currently provide safe upcasting methods for types However, because of some technical problems, we don't currently provide safe upcasting methods for types
that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion. that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion.
@ -334,6 +335,7 @@ impl DictWithCounter {
fn new() -> Self { fn new() -> Self {
Self::default() Self::default()
} }
fn set(mut self_: PyRefMut<'_, Self>, key: String, value: &PyAny) -> PyResult<()> { fn set(mut self_: PyRefMut<'_, Self>, key: String, value: &PyAny) -> PyResult<()> {
self_.counter.entry(key.clone()).or_insert(0); self_.counter.entry(key.clone()).or_insert(0);
let py = self_.py(); let py = self_.py();
@ -371,6 +373,42 @@ impl SubClass {
} }
``` ```
The `__new__` constructor of a native base class is called implicitly when
creating a new instance from Python. Be sure to accept arguments in the
`#[new]` method that you want the base class to get, even if they are not used
in that `fn`:
```rust
# #[allow(dead_code)]
# #[cfg(not(Py_LIMITED_API))] {
# use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyclass(extends=PyDict)]
struct MyDict {
private: i32,
}
#[pymethods]
impl MyDict {
#[new]
#[pyo3(signature = (*args, **kwargs))]
fn new(args: &PyAny, kwargs: Option<&PyAny>) -> Self {
Self { private: 0 }
}
// some custom methods that use `private` here...
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyDict>();
# pyo3::py_run!(py, cls, "cls(a=1, b=2)")
# });
# }
```
Here, the `args` and `kwargs` allow creating instances of the subclass passing
initial items, such as `MyDict(item_sequence)` or `MyDict(a=1, b=2)`.
## Object properties ## Object properties
PyO3 supports two ways to add properties to your `#[pyclass]`: PyO3 supports two ways to add properties to your `#[pyclass]`: