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
|
example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See
|
||||||
:func:`itertools.islice` for an alternate version that returns an iterator.
|
: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)
|
.. function:: sorted(iterable, /, *, key=None, reverse=False)
|
||||||
|
|
||||||
|
|
|
@ -419,11 +419,6 @@ class CAPITest(unittest.TestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
_testcapi.sequence_set_slice(None, 1, 3, 'xy')
|
_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):
|
def test_sequence_del_slice(self):
|
||||||
# Correct case:
|
# Correct case:
|
||||||
data = [1, 2, 3, 4, 5]
|
data = [1, 2, 3, 4, 5]
|
||||||
|
@ -459,7 +454,7 @@ class CAPITest(unittest.TestCase):
|
||||||
_testcapi.sequence_del_slice(None, 1, 3)
|
_testcapi.sequence_del_slice(None, 1, 3)
|
||||||
|
|
||||||
mapping = {1: 'a', 2: 'b', 3: 'c'}
|
mapping = {1: 'a', 2: 'b', 3: 'c'}
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(KeyError):
|
||||||
_testcapi.sequence_del_slice(mapping, 1, 3)
|
_testcapi.sequence_del_slice(mapping, 1, 3)
|
||||||
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})
|
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})
|
||||||
|
|
||||||
|
|
|
@ -707,7 +707,7 @@ plain ol' Python and is guaranteed to be available.
|
||||||
|
|
||||||
>>> import builtins
|
>>> import builtins
|
||||||
>>> tests = doctest.DocTestFinder().find(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
|
True
|
||||||
>>> real_tests = [t for t in tests if len(t.examples) > 0]
|
>>> real_tests = [t for t in tests if len(t.examples) > 0]
|
||||||
>>> len(real_tests) # objects that actually have doctests
|
>>> 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)")
|
self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")
|
||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
# Verify clearing of SF bug #800796
|
self.assertEqual(hash(slice(5)), slice(5).__hash__())
|
||||||
self.assertRaises(TypeError, hash, slice(5))
|
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):
|
with self.assertRaises(TypeError):
|
||||||
slice(5).__hash__()
|
hash(slice(1, 2, []))
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
hash(slice(4, {}))
|
||||||
|
|
||||||
def test_cmp(self):
|
def test_cmp(self):
|
||||||
s1 = slice(1, 2, 3)
|
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;
|
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 = {
|
PyTypeObject PySlice_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"slice", /* Name of this type */
|
"slice", /* Name of this type */
|
||||||
|
@ -642,7 +678,7 @@ PyTypeObject PySlice_Type = {
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
0, /* tp_as_sequence */
|
0, /* tp_as_sequence */
|
||||||
0, /* tp_as_mapping */
|
0, /* tp_as_mapping */
|
||||||
PyObject_HashNotImplemented, /* tp_hash */
|
(hashfunc)slicehash, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
|
|
|
@ -288,7 +288,7 @@ error:
|
||||||
|
|
||||||
/* Hash for tuples. This is a slightly simplified version of the xxHash
|
/* Hash for tuples. This is a slightly simplified version of the xxHash
|
||||||
non-cryptographic hash:
|
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
|
- we drop the final mixing since this is just a permutation of the
|
||||||
output space: it does not help against collisions.
|
output space: it does not help against collisions.
|
||||||
- at the end, we mangle the length with a single constant.
|
- at the end, we mangle the length with a single constant.
|
||||||
|
|
Loading…
Reference in New Issue