Set __hash__ to None for types that defines __eq__, but not __hash__ (#2291)

fixes #2191
This commit is contained in:
Sergei Izmailov
2020-07-27 02:44:25 +03:00
committed by GitHub
parent aab701399a
commit 7b067cc387
3 changed files with 56 additions and 1 deletions

View File

@@ -187,6 +187,38 @@ TEST_SUBMODULE(operators, m) {
.def(py::self *= int())
.def_readwrite("b", &NestC::b);
m.def("get_NestC", [](const NestC &c) { return c.value; });
// test_overriding_eq_reset_hash
// #2191 Overriding __eq__ should set __hash__ to None
struct Comparable {
int value;
bool operator==(const Comparable& rhs) const {return value == rhs.value;}
};
struct Hashable : Comparable {
explicit Hashable(int value): Comparable{value}{};
size_t hash() const { return static_cast<size_t>(value); }
};
struct Hashable2 : Hashable {
using Hashable::Hashable;
};
py::class_<Comparable>(m, "Comparable")
.def(py::init<int>())
.def(py::self == py::self);
py::class_<Hashable>(m, "Hashable")
.def(py::init<int>())
.def(py::self == py::self)
.def("__hash__", &Hashable::hash);
// define __hash__ before __eq__
py::class_<Hashable2>(m, "Hashable2")
.def("__hash__", &Hashable::hash)
.def(py::init<int>())
.def(py::self == py::self);
}
#ifndef _MSC_VER

View File

@@ -127,3 +127,19 @@ def test_nested():
assert abase.value == 42
del abase, b
pytest.gc_collect()
def test_overriding_eq_reset_hash():
assert m.Comparable(15) is not m.Comparable(15)
assert m.Comparable(15) == m.Comparable(15)
with pytest.raises(TypeError):
hash(m.Comparable(15)) # TypeError: unhashable type: 'm.Comparable'
for hashable in (m.Hashable, m.Hashable2):
assert hashable(15) is not hashable(15)
assert hashable(15) == hashable(15)
assert hash(hashable(15)) == 15
assert hash(hashable(15)) == hash(hashable(15))