mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-21 20:55:11 +00:00
Access C++ hash functions from Python and vice versa (#1034)
There are two separate additions: 1. `py::hash(obj)` is equivalent to the Python `hash(obj)`. 2. `.def(hash(py::self))` registers the hash function defined by `std::hash<T>` as the Python hash function.
This commit is contained in:
parent
29b99a11a4
commit
37de2da9dd
@ -243,6 +243,11 @@ v2.2.0 (Not yet released)
|
||||
* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7.
|
||||
`#821 <https://github.com/pybind/pybind11/pull/821>`_.
|
||||
|
||||
* Added ``py::hash`` to fetch the hash value of Python objects, and
|
||||
``.def(hash(py::self))`` to provide the C++ ``std::hash`` as the Python
|
||||
``__hash__`` method.
|
||||
`#1034 <https://github.com/pybind/pybind11/pull/1034>`_.
|
||||
|
||||
* Fixed ``__truediv__`` on Python 2 and ``__itruediv__`` on Python 3.
|
||||
`#867 <https://github.com/pybind/pybind11/pull/867>`_.
|
||||
|
||||
|
@ -28,7 +28,7 @@ enum op_id : int {
|
||||
op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le,
|
||||
op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift,
|
||||
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero,
|
||||
op_repr, op_truediv, op_itruediv
|
||||
op_repr, op_truediv, op_itruediv, op_hash
|
||||
};
|
||||
|
||||
enum op_type : int {
|
||||
@ -148,6 +148,7 @@ PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
|
@ -390,6 +390,13 @@ inline void setattr(handle obj, handle name, handle value) {
|
||||
inline void setattr(handle obj, const char *name, handle value) {
|
||||
if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); }
|
||||
}
|
||||
|
||||
inline ssize_t hash(handle obj) {
|
||||
auto h = PyObject_Hash(obj.ptr());
|
||||
if (h == -1) { throw error_already_set(); }
|
||||
return h;
|
||||
}
|
||||
|
||||
/// @} python_builtins
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/operators.h>
|
||||
#include <functional>
|
||||
|
||||
class Vector2 {
|
||||
public:
|
||||
@ -53,6 +54,14 @@ int operator+(const C2 &, const C2 &) { return 22; }
|
||||
int operator+(const C2 &, const C1 &) { return 21; }
|
||||
int operator+(const C1 &, const C2 &) { return 12; }
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Vector2> {
|
||||
// Not a good hash function, but easy to test
|
||||
size_t operator()(const Vector2 &) { return 4; }
|
||||
};
|
||||
}
|
||||
|
||||
TEST_SUBMODULE(operators, m) {
|
||||
|
||||
// test_operator_overloading
|
||||
@ -77,6 +86,7 @@ TEST_SUBMODULE(operators, m) {
|
||||
.def(float() * py::self)
|
||||
.def(float() / py::self)
|
||||
.def("__str__", &Vector2::toString)
|
||||
.def(hash(py::self))
|
||||
;
|
||||
|
||||
m.attr("Vector") = m.attr("Vector2");
|
||||
|
@ -35,6 +35,8 @@ def test_operator_overloading():
|
||||
v2 /= v1
|
||||
assert str(v2) == "[2.000000, 8.000000]"
|
||||
|
||||
assert hash(v1) == 4
|
||||
|
||||
cstats = ConstructorStats.get(m.Vector2)
|
||||
assert cstats.alive() == 2
|
||||
del v1
|
||||
|
@ -261,4 +261,6 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
});
|
||||
|
||||
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
|
||||
|
||||
m.def("hash_function", [](py::object obj) { return py::hash(obj); });
|
||||
}
|
||||
|
@ -220,3 +220,19 @@ def test_print(capture):
|
||||
if debug_enabled else
|
||||
"arguments to Python object (compile in debug mode for details)"
|
||||
)
|
||||
|
||||
|
||||
def test_hash():
|
||||
class Hashable(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __hash__(self):
|
||||
return self.value
|
||||
|
||||
class Unhashable(object):
|
||||
__hash__ = None
|
||||
|
||||
assert m.hash_function(Hashable(42)) == 42
|
||||
with pytest.raises(TypeError):
|
||||
m.hash_function(Unhashable())
|
||||
|
Loading…
Reference in New Issue
Block a user