mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +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.
|
* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7.
|
||||||
`#821 <https://github.com/pybind/pybind11/pull/821>`_.
|
`#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.
|
* Fixed ``__truediv__`` on Python 2 and ``__itruediv__`` on Python 3.
|
||||||
`#867 <https://github.com/pybind/pybind11/pull/867>`_.
|
`#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_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_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_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 {
|
enum op_type : int {
|
||||||
@ -148,6 +148,7 @@ PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
|||||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(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(invert, operator~, (~l))
|
||||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) 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) {
|
inline void setattr(handle obj, const char *name, handle value) {
|
||||||
if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); }
|
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
|
/// @} python_builtins
|
||||||
|
|
||||||
NAMESPACE_BEGIN(detail)
|
NAMESPACE_BEGIN(detail)
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "pybind11_tests.h"
|
#include "pybind11_tests.h"
|
||||||
#include "constructor_stats.h"
|
#include "constructor_stats.h"
|
||||||
#include <pybind11/operators.h>
|
#include <pybind11/operators.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class Vector2 {
|
class Vector2 {
|
||||||
public:
|
public:
|
||||||
@ -53,6 +54,14 @@ int operator+(const C2 &, const C2 &) { return 22; }
|
|||||||
int operator+(const C2 &, const C1 &) { return 21; }
|
int operator+(const C2 &, const C1 &) { return 21; }
|
||||||
int operator+(const C1 &, const C2 &) { return 12; }
|
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_SUBMODULE(operators, m) {
|
||||||
|
|
||||||
// test_operator_overloading
|
// test_operator_overloading
|
||||||
@ -77,6 +86,7 @@ TEST_SUBMODULE(operators, m) {
|
|||||||
.def(float() * py::self)
|
.def(float() * py::self)
|
||||||
.def(float() / py::self)
|
.def(float() / py::self)
|
||||||
.def("__str__", &Vector2::toString)
|
.def("__str__", &Vector2::toString)
|
||||||
|
.def(hash(py::self))
|
||||||
;
|
;
|
||||||
|
|
||||||
m.attr("Vector") = m.attr("Vector2");
|
m.attr("Vector") = m.attr("Vector2");
|
||||||
|
@ -35,6 +35,8 @@ def test_operator_overloading():
|
|||||||
v2 /= v1
|
v2 /= v1
|
||||||
assert str(v2) == "[2.000000, 8.000000]"
|
assert str(v2) == "[2.000000, 8.000000]"
|
||||||
|
|
||||||
|
assert hash(v1) == 4
|
||||||
|
|
||||||
cstats = ConstructorStats.get(m.Vector2)
|
cstats = ConstructorStats.get(m.Vector2)
|
||||||
assert cstats.alive() == 2
|
assert cstats.alive() == 2
|
||||||
del v1
|
del v1
|
||||||
|
@ -261,4 +261,6 @@ TEST_SUBMODULE(pytypes, m) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
|
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
|
if debug_enabled else
|
||||||
"arguments to Python object (compile in debug mode for details)"
|
"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