mirror of https://github.com/python/cpython
GH-84783: Make the slice object hashable (GH-101264)
This commit is contained in:
parent
5170caf305
commit
61f1e67c6f
|
@ -1635,6 +1635,9 @@ are always available. They are listed here in alphabetical order.
|
|||
example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See
|
||||
:func:`itertools.islice` for an alternate version that returns an iterator.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Slice objects are now :term:`hashable` (provided :attr:`~slice.start`,
|
||||
:attr:`~slice.stop`, and :attr:`~slice.step` are hashable).
|
||||
|
||||
.. function:: sorted(iterable, /, *, key=None, reverse=False)
|
||||
|
||||
|
|
|
@ -419,11 +419,6 @@ class CAPITest(unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
_testcapi.sequence_set_slice(None, 1, 3, 'xy')
|
||||
|
||||
mapping = {1: 'a', 2: 'b', 3: 'c'}
|
||||
with self.assertRaises(TypeError):
|
||||
_testcapi.sequence_set_slice(mapping, 1, 3, 'xy')
|
||||
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})
|
||||
|
||||
def test_sequence_del_slice(self):
|
||||
# Correct case:
|
||||
data = [1, 2, 3, 4, 5]
|
||||
|
@ -459,7 +454,7 @@ class CAPITest(unittest.TestCase):
|
|||
_testcapi.sequence_del_slice(None, 1, 3)
|
||||
|
||||
mapping = {1: 'a', 2: 'b', 3: 'c'}
|
||||
with self.assertRaises(TypeError):
|
||||
with self.assertRaises(KeyError):
|
||||
_testcapi.sequence_del_slice(mapping, 1, 3)
|
||||
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})
|
||||
|
||||
|
|
|
@ -707,7 +707,7 @@ plain ol' Python and is guaranteed to be available.
|
|||
|
||||
>>> import builtins
|
||||
>>> tests = doctest.DocTestFinder().find(builtins)
|
||||
>>> 825 < len(tests) < 845 # approximate number of objects with docstrings
|
||||
>>> 830 < len(tests) < 850 # approximate number of objects with docstrings
|
||||
True
|
||||
>>> real_tests = [t for t in tests if len(t.examples) > 0]
|
||||
>>> len(real_tests) # objects that actually have doctests
|
||||
|
|
|
@ -80,10 +80,16 @@ class SliceTest(unittest.TestCase):
|
|||
self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
|
||||
|
||||
def test_hash(self):
|
||||
# Verify clearing of SF bug #800796
|
||||
self.assertRaises(TypeError, hash, slice(5))
|
||||
self.assertEqual(hash(slice(5)), slice(5).__hash__())
|
||||
self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__())
|
||||
self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__())
|
||||
self.assertNotEqual(slice(5), slice(6))
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
slice(5).__hash__()
|
||||
hash(slice(1, 2, []))
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
hash(slice(4, {}))
|
||||
|
||||
def test_cmp(self):
|
||||
s1 = slice(1, 2, 3)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Make the slice object hashable.
|
|
@ -628,6 +628,42 @@ slice_traverse(PySliceObject *v, visitproc visit, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* code based on tuplehash() of Objects/tupleobject.c */
|
||||
#if SIZEOF_PY_UHASH_T > 4
|
||||
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL)
|
||||
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL)
|
||||
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL)
|
||||
#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */
|
||||
#else
|
||||
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL)
|
||||
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL)
|
||||
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL)
|
||||
#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */
|
||||
#endif
|
||||
|
||||
static Py_hash_t
|
||||
slicehash(PySliceObject *v)
|
||||
{
|
||||
Py_uhash_t acc = _PyHASH_XXPRIME_5;
|
||||
#define _PyHASH_SLICE_PART(com) { \
|
||||
Py_uhash_t lane = PyObject_Hash(v->com); \
|
||||
if(lane == (Py_uhash_t)-1) { \
|
||||
return -1; \
|
||||
} \
|
||||
acc += lane * _PyHASH_XXPRIME_2; \
|
||||
acc = _PyHASH_XXROTATE(acc); \
|
||||
acc *= _PyHASH_XXPRIME_1; \
|
||||
}
|
||||
_PyHASH_SLICE_PART(start);
|
||||
_PyHASH_SLICE_PART(stop);
|
||||
_PyHASH_SLICE_PART(step);
|
||||
#undef _PyHASH_SLICE_PART
|
||||
if(acc == (Py_uhash_t)-1) {
|
||||
return 1546275796;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
PyTypeObject PySlice_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"slice", /* Name of this type */
|
||||
|
@ -642,7 +678,7 @@ PyTypeObject PySlice_Type = {
|
|||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
PyObject_HashNotImplemented, /* tp_hash */
|
||||
(hashfunc)slicehash, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
PyObject_GenericGetAttr, /* tp_getattro */
|
||||
|
|
|
@ -288,7 +288,7 @@ error:
|
|||
|
||||
/* Hash for tuples. This is a slightly simplified version of the xxHash
|
||||
non-cryptographic hash:
|
||||
- we do not use any parallellism, there is only 1 accumulator.
|
||||
- we do not use any parallelism, there is only 1 accumulator.
|
||||
- we drop the final mixing since this is just a permutation of the
|
||||
output space: it does not help against collisions.
|
||||
- at the end, we mangle the length with a single constant.
|
||||
|
|
Loading…
Reference in New Issue