Skip to content

Commit 8ded755

Browse files
committed
add allow_slice
1 parent 03c1b22 commit 8ded755

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

pandas/core/dtypes/inference.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]:
393393
allow_slice: bool or None
394394
- If True: return True if the object is hashable (including slices).
395395
- If False: return True if the object is hashable and not a slice.
396-
- If None: return True of the object is hashable. without checking for slice type.
396+
- If None: return True if the object is hashable. without checking for slice type.
397397
398398
Returns
399399
-------
@@ -426,12 +426,20 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]:
426426
# Reconsider this decision once this numpy bug is fixed:
427427
# https://github.com/numpy/numpy/issues/5562
428428

429+
def _contains_slice(x: object) -> bool:
430+
# Check if object is a slice or a tuple containing a slice
431+
if isinstance(x, tuple):
432+
return any(isinstance(v, slice) for v in x)
433+
elif isinstance(x, slice):
434+
return True
435+
return False
436+
429437
try:
430438
hash(obj)
431439
except TypeError:
432440
return False
433-
else:
434-
if allow_slice is False and isinstance(obj, slice):
441+
else:
442+
if allow_slice is False and _contains_slice(obj):
435443
return False
436444
return True
437445

pandas/tests/dtypes/test_inference.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,17 +451,47 @@ class UnhashableClass1:
451451
class UnhashableClass2:
452452
def __hash__(self):
453453
raise TypeError("Not hashable")
454+
455+
class HashableSlice:
456+
def __init__(self, start, stop, step=None):
457+
self.slice = slice(start, stop, step)
458+
459+
def __eq__(self, other):
460+
return isinstance(other, HashableSlice) and self.slice == other.slice
461+
462+
def __hash__(self):
463+
return hash((self.slice.start, self.slice.stop, self.slice.step))
464+
465+
def __repr__(self):
466+
return f"HashableSlice({self.slice.start}, {self.slice.stop}, {self.slice.step})"
467+
454468

455469
hashable = (1, 3.14, np.float64(3.14), "a", (), (1,), HashableClass())
456-
not_hashable = ([], UnhashableClass1())
470+
not_hashable = ([], UnhashableClass1(), slice(1, 2, 3))
457471
abc_hashable_not_really_hashable = (([],), UnhashableClass2())
472+
hashable_slice = (HashableSlice(1, 2), HashableSlice(1, 2, 3))
473+
tuple_with_slice = ((slice(1, 2), 3), 1, "a")
458474

459475
for i in hashable:
460476
assert inference.is_hashable(i)
477+
assert inference.is_hashable(i, allow_slice=True)
478+
assert inference.is_hashable(i, allow_slice=False)
461479
for i in not_hashable:
462480
assert not inference.is_hashable(i)
481+
assert not inference.is_hashable(i, allow_slice=True)
482+
assert not inference.is_hashable(i, allow_slice=False)
463483
for i in abc_hashable_not_really_hashable:
464484
assert not inference.is_hashable(i)
485+
assert not inference.is_hashable(i, allow_slice=True)
486+
assert not inference.is_hashable(i, allow_slice=False)
487+
for i in hashable_slice:
488+
assert inference.is_hashable(i)
489+
assert inference.is_hashable(i, allow_slice=True)
490+
assert inference.is_hashable(i, allow_slice=False)
491+
492+
assert not inference.is_hashable(tuple_with_slice)
493+
assert not inference.is_hashable(tuple_with_slice, allow_slice=True)
494+
assert not inference.is_hashable(tuple_with_slice, allow_slice=False)
465495

466496
# numpy.array is no longer collections.abc.Hashable as of
467497
# https://github.com/numpy/numpy/pull/5326, just test

0 commit comments

Comments
 (0)