Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for classattr and getter on pyclass attribute of the same name #4786

Open
acroz opened this issue Dec 10, 2024 · 2 comments
Open

Support for classattr and getter on pyclass attribute of the same name #4786

acroz opened this issue Dec 10, 2024 · 2 comments

Comments

@acroz
Copy link

acroz commented Dec 10, 2024

Hi, I'd like to make a pyclass with a classattr and regular attribute with the same name. See this example:

#[pyclass]
struct PyO3Class {
    #[pyo3(get, set)]
    attr: i32,
}

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

    #[classattr]
    fn attr() -> String {
        "From the class".to_string()
    }
}

My expectation based on standard Python behaviour is that when I access .attr on a class, I should get the string from the classattr, whereas on an instance I get the integer value out from the instance.

See this Python code demonstrating this:

class PythonClass:
    attr: str = "From the class"

    def __init__(self, attr: int) -> None:
        self.attr = attr


python_instance = PythonClass(123)
print("PythonClass.attr", PythonClass.attr)
print("instance.attr", python_instance.attr)

outputting:

PythonClass.attr From the class
instance.attr 123

However, from PyO3 class above, the equivalent code:

from example import PyO3Class

pyo3_instance = PyO3Class(123)
print("PyO3Class.attr", PyO3Class.attr)
print("pyo3_instance.attr", pyo3_instance.attr)

outputs:

PyO3Class.attr From the class
pyo3_instance.attr From the class

I've tried a few other patterns to try and get this to work:

  • Implement a #[getter] explicitly and play with the order
  • Do inheritance and have the classattr in the base class
  • Call PyO3Class::type_object(py).setattr("attr", "From the class") on module initialisation

However all resulted in the same behaviour.

I've opened this issue as suggested by @davidhewitt on Discord.

@acroz
Copy link
Author

acroz commented Dec 10, 2024

Here is a working example of the above:
example.tar.gz

@acroz
Copy link
Author

acroz commented Dec 10, 2024

For context, I want different behaviours when accessing the attribute on the class and from an instance as I'm trying to implement a PySpark-inspired interface, see this Python pseudo-code:

class FilterBuilder:
    def __init__(self, type_: str, attribute: str) -> None:
        ...

    def __eq__(self, value) -> Filter: ...
    def __ne__(self, value) -> Filter: ...
    def __gt__(self, value) -> Filter: ...
    def __lt__(self, value) -> Filter: ...
    # etc.

class Position:
    x: FilterBuilder = FilterBuilder("Position", "x")
    y: FilterBuilder = FilterBuilder("Position", "y")

    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

class Collection:
    def filter(self, filter: Filter) -> Collection ....

Used like:

collection: Collection
collection.filter(Position.x == 10)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant