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 classmethod #1693

Open
asadchev opened this issue Feb 13, 2019 · 5 comments · May be fixed by #4158
Open

Support classmethod #1693

asadchev opened this issue Feb 13, 2019 · 5 comments · May be fixed by #4158

Comments

@asadchev
Copy link

I have a workaround to create classmethods in PyBind.

  template<class Func, typename ... Args>
  pybind11::object classmethod(Func f, Args ... args) {
    pybind11::object cf = pybind11::cpp_function(f, args...);
    return pybind11::object(PyClassMethod_New(cf.ptr()), true);
  }

used like this

void foo(py::object cls, ...);
cls.attr("foo") = classmethod(&foo);

Be useful to have such mechanism in pybind proper

@henryiii
Copy link
Collaborator

@YannickJadoul were you going to look into this? This is a possible workaround without calling CPython API:

class Example {
    int x;
    Example(int x) : x(x) {}
public:
    static Example make_x(int y) { return Example(y); }
    int get_x() const { return x; }
};

PYBIND11_MODULE(tmp, m) {
    py::class_<Example>(m, "Example")
        .def("get_x", &Example::get_x)
        .def_property_readonly_static("make_x", [](py::object){
              return py::cpp_function([](int x){return Example::make_x(3);
        });});
}

@YannickJadoul
Copy link
Collaborator

YannickJadoul commented Feb 26, 2021

@henryiii, I was. I thought it was easy as the staticmethod PR I contributed a few years ago, but turns out to not fully be the case. The question is a bit what to do with the first argument to a classmethod. Should it be a py::type?

@YannickJadoul YannickJadoul removed their assignment Feb 26, 2021
@Skylion007
Copy link
Collaborator

@YannickJadoul Pretty sure it should be a py::type.

@Skylion007
Copy link
Collaborator

@YannickJadoul were you going to look into this? This is a possible workaround without calling CPython API:

class Example {
    int x;
    Example(int x) : x(x) {}
public:
    static Example make_x(int y) { return Example(y); }
    int get_x() const { return x; }
};

PYBIND11_MODULE(tmp, m) {
    py::class_<Example>(m, "Example")
        .def("get_x", &Example::get_x)
        .def_property_readonly_static("make_x", [](py::object){
              return py::cpp_function([](int x){return Example::make_x(3);
        });});
}

If follow the implementation of property_readonly_static though it does eventually call the C++ API. We should just officially support it IMO as this is a lot of indirection and all our static methods call attr anyway.

@Skylion007
Copy link
Collaborator

Turns out this might actually be a bit easier than I thought. new is not ACTUALLY a classmethod as classmethod even though it is one of the most common usecases for them. As such, the overloading of new with classmethod is generating errors since we don't allow overriding an instance method with a static method or vice versa. That means implementing it shouldn't be too bad. The harder part is writing the appropriate tests / usecases. Especially any use cases where a static method would not work well currently.

I would like to see an actual usecase where though where the class parameter of the classmethod is used though.

As for the callsign, the class argument to the classmethod can just be a py::object.

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

Successfully merging a pull request may close this issue.

4 participants