Why does `slice` have to be unhashable?
Question:
Why doesn’t python make slice
hashable? In my simple mind you can simply xor
its start
, stop
, and step
and you will have a good hash.
It will be very useful when we want unordered sets of slices.
Answers:
slice
objects aren’t hashable because it’s possible for a slice
to be made up of mutable (unhashable) objects, such as a list
.
slice()
doesn’t require that the arguments be integers – any object type is allowed. This is perfectly legal:
slice([1, 2, 3], [4, 5, 6])
though it’s not very useful, since using that slice
object for indexing generally won’t work:
>>> s = slice([1,2,3], [4,5,6])
>>> a = [10, 11, 12]
>>> a[s]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: slice indices must be integers or None or have an __index__ method
It doesn’t have to be, it’s an accident of history. Slices were made comparable back in 2001, but they weren’t made hashable because it caused an issue to do with setting slices as keys in dicts. So no hash method that returns hash(tuple(start, stop, step))
was added, although they’re otherwise treated like a tuple of their start, stop and step values.
Recently this was raised again, considered a bug and fixed. As of version 3.12, this 22 year old problem will finally go away!
For a workaround, you can use this (source) for now, and remove it after everyone is using 3.12 or later:
class hashable_slice:
"""
Hashable slice wrapper. Can be removed after Python 3.12
"""
__slots__ = ["slice"]
def __init__(self, s: slice):
self.slice = s
def __hash__(self):
return hash((self.slice.start, self.slice.stop, self.slice.step))
def __eq__(self, other):
return other == self.slice
Why doesn’t python make slice
hashable? In my simple mind you can simply xor
its start
, stop
, and step
and you will have a good hash.
It will be very useful when we want unordered sets of slices.
slice
objects aren’t hashable because it’s possible for a slice
to be made up of mutable (unhashable) objects, such as a list
.
slice()
doesn’t require that the arguments be integers – any object type is allowed. This is perfectly legal:
slice([1, 2, 3], [4, 5, 6])
though it’s not very useful, since using that slice
object for indexing generally won’t work:
>>> s = slice([1,2,3], [4,5,6])
>>> a = [10, 11, 12]
>>> a[s]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: slice indices must be integers or None or have an __index__ method
It doesn’t have to be, it’s an accident of history. Slices were made comparable back in 2001, but they weren’t made hashable because it caused an issue to do with setting slices as keys in dicts. So no hash method that returns hash(tuple(start, stop, step))
was added, although they’re otherwise treated like a tuple of their start, stop and step values.
Recently this was raised again, considered a bug and fixed. As of version 3.12, this 22 year old problem will finally go away!
For a workaround, you can use this (source) for now, and remove it after everyone is using 3.12 or later:
class hashable_slice:
"""
Hashable slice wrapper. Can be removed after Python 3.12
"""
__slots__ = ["slice"]
def __init__(self, s: slice):
self.slice = s
def __hash__(self):
return hash((self.slice.start, self.slice.stop, self.slice.step))
def __eq__(self, other):
return other == self.slice