Skip to content

[BUG]: __getattr__ and base class order #3804

Open
@petersteneteg

Description

@petersteneteg

Required prerequisites

Problem description

In the binding code below __getattr__ will only be found in the derived class if we do:
py::class_<Derived, Base, OtherBase>(m, "Derived")
if we instead do:
py::class_<Derived, OtherBase, Base>(m, "Derived")
note the order of the base classes switched,
the bound __getattr__ will never be called.

From what I understand from the documentation (https://pybind11.readthedocs.io/en/latest/advanced/classes.html#multiple-inheritance) the order of the base classes should not matter?

This is tested with the latest master as of writing.

Reproducible example code

// binding code
#include <pybind11/pybind11.h>

namespace py = pybind11;

struct Base {
    virtual ~Base() = default;
    int base() const { return 0; }
};
struct OtherBase {
    int other() const { return 3; }
};
struct Derived : Base, OtherBase{
    int id() const { return 2; }
};

PYBIND11_MODULE(bar, m) {
    py::class_<Base>(m, "Base")
        .def(py::init<>())
        .def("base", &Base::base)
        .def("__getattr__", [](Base&, std::string key) {
            return "Base GetAttr: " + key;
        });

    py::class_<OtherBase>(m, "OtherBase")
        .def("other", &OtherBase::other);

    py::class_<Derived, OtherBase, Base>(m, "Derived")
        .def(py::init<>())
        .def("id", &Derived::id);
}

// test code
import bar

d = bar.Derived()
print(f"d.base(): {d.base()}")
print(f"d.other(): {d.other()}")
print(f"d.id(): {d.id()}")
print(f"d.prop: {d.prop}") # this will fail (if Base is not the first base class)

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocs or GitHub info

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions