I am using PyO3 (v0.22.2) / Rust to write something a little similar to defaultdict
, but with an integer default rather than a callback:
>>> from my_package.pyo3_ext import PyDefaultDict
>>> d = PyDefaultDict(42, a=1, b=2)
>>> d['a']
1
>>> d['z']
42
I am trying to subclass the built-in Python dict
with extends=PyDict
, as per examples:
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyclass(extends=PyDict)]
pub struct PyDefaultDict {
x: i32,
}
#[pymethods]
impl PyDefaultDict {
#[new]
#[pyo3(signature = (x, *_args, **_kwargs))]
fn new(x: i32, _args: &Bound<'_, PyAny>, _kwargs: Option<&Bound<'_, PyAny>>) -> Self {
Self { x }
}
fn get_x(&self) -> i32 {
self.x
}
}
This compiles, but when I try to call the constructor in Python, I get the following error:
>>> x = PyDefaultDict(42)
TypeError: 'int' object is not iterable
I am referring to the PyO3 user guide::Inheritance section. This actually has an example of ensuring that _args
and _kwargs
are passed through to the base class, but it does not demonstrate how to provide a custom __init__()
parameter via new()
.
Note that even if I remove x
from my struct, so that all I’m doing is accepting an i32
parameter in new()
, and not trying to assign it to anything, I still get the same error.
I also tried accepting a PyAny
and then extracting to an i32
:
fn new(x: &Bound<'_, PyAny>, _args: &Bound<'_, PyAny>, _kwargs: Option<&Bound<'_, PyAny>>) -> Self {
Self { _x: x.extract().unwrap() }
}
Alas, this gives me the same error.
What’s the little piece of knowledge I am missing here, to help solve this?
Note, the base example, without the new parameter x
, works just like a normal dict
, so I’m basing my work on this:
#[pyclass(extends=PyDict)]
pub struct PyDefaultDict {}
#[pymethods]
impl PyDefaultDict {
#[new]
#[pyo3(signature = (*_args, **_kwargs))]
fn new(_args: &Bound<'_, PyAny>, _kwargs: Option<&Bound<'_, PyAny>>) -> Self {
Self {}
}
}