PyO3 / pyo3

Rust bindings for the Python interpreter
https://pyo3.rs
Other
11.47k stars 694 forks source link

`a.b.c += 1` does not work #4204

Closed fzyzcjy closed 1 month ago

fzyzcjy commented 1 month ago

Bug Description

Hi thanks for the tool! However, sometimes it has a bit confusing semantics. If I understand correctly, it is because that, the auto generated getter indeed does a clone to the underlying object. Thus, in the example below, modification of my_class.sub.num acts on a copy of MySubClass object, instead of the original object on my_class.

Steps to Reproduce

use pyo3::prelude::*;

#[pyclass]
struct MyClass {
    #[pyo3(get, set)]
    sub: MySubClass,
}

#[pymethods]
impl MyClass {
    #[new]
    fn new() -> Self {
        MyClass { sub: MySubClass::new(100) }
    }
}

#[pyclass]
#[derive(Clone)]
struct MySubClass {
    #[pyo3(get, set)]
    num: i32,
}

#[pymethods]
impl MySubClass {
    #[new]
    fn new(num: i32) -> Self {
        MySubClass { num }
    }
}

/// A Python module implemented in Rust.
#[pymodule]
fn hello_pyo3(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<MyClass>()?;
    m.add_class::<MySubClass>()?;
    Ok(())
}
import hello_pyo3

my_class = hello_pyo3.MyClass()
print(f'{my_class=} {my_class.sub=} {my_class.sub.num=}')

// Unexpected
my_class.sub.num += 10000
print(f'{my_class=} {my_class.sub=} {my_class.sub.num=}')

// Expected
my_class.sub = hello_pyo3.MySubClass(num=200)
print(f'{my_class=} {my_class.sub=} {my_class.sub.num=}')
(base) ➜  hello_pyo3 maturin develop && python hi.py
🔗 Found pyo3 bindings
🐍 Found CPython 3.9 at /Users/tom/opt/anaconda3/bin/python
   Compiling hello_pyo3 v0.1.0 (/Volumes/MyExternal/ExternalRefCode/hello_pyo3)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
📦 Built wheel for CPython 3.9 to /var/folders/j5/j6ymn7yd70564hzt31pq_0g80000gn/T/.tmpDJxqpB/hello_pyo3-0.1.0-cp39-cp39-macosx_10_12_x86_64.whl
🛠 Installed hello_pyo3-0.1.0
my_class=<builtins.MyClass object at 0x7f7ff00c1e70> my_class.sub=<builtins.MySubClass object at 0x7f80100e83f0> my_class.sub.num=100
my_class=<builtins.MyClass object at 0x7f7ff00c1e70> my_class.sub=<builtins.MySubClass object at 0x7f80100e83f0> my_class.sub.num=100
my_class=<builtins.MyClass object at 0x7f7ff00c1e70> my_class.sub=<builtins.MySubClass object at 0x7f80100e83f0> my_class.sub.num=200

Backtrace

No response

Your operating system and version

-

Your Python version (python --version)

-

Your Rust version (rustc --version)

-

Your PyO3 version

latest

How did you install python? Did you use a virtualenv?

-

Additional Info

No response

messense commented 1 month ago

This is a well-known issue, see https://pyo3.rs/v0.21.2/faq#pyo3get-clones-my-field

fzyzcjy commented 1 month ago

Thank you for the super fast reply!