Skip to content

Commit ab42f85

Browse files
authored
REF: implement LocationIndexer._tupleize_axis_indexer (#45378)
1 parent 6cc5584 commit ab42f85

File tree

1 file changed

+35
-15
lines changed

1 file changed

+35
-15
lines changed

pandas/core/indexing.py

+35-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
TYPE_CHECKING,
66
Hashable,
77
Sequence,
8+
final,
89
)
910
import warnings
1011

@@ -626,6 +627,7 @@ class _LocationIndexer(NDFrameIndexerBase):
626627
_valid_types: str
627628
axis = None
628629

630+
@final
629631
def __call__(self, axis=None):
630632
# we need to return a copy of ourselves
631633
new_self = type(self)(self.name, self.obj)
@@ -640,14 +642,15 @@ def _get_setitem_indexer(self, key):
640642
Convert a potentially-label-based key into a positional indexer.
641643
"""
642644
if self.name == "loc":
645+
# always holds here bc iloc overrides _get_setitem_indexer
643646
self._ensure_listlike_indexer(key)
644647

645648
if isinstance(key, tuple):
646649
for x in key:
647650
check_deprecated_indexers(x)
648651

649652
if self.axis is not None:
650-
return self._convert_tuple(key)
653+
key = self._tupleize_axis_indexer(key)
651654

652655
ax = self.obj._get_axis(0)
653656

@@ -658,13 +661,26 @@ def _get_setitem_indexer(self, key):
658661

659662
if isinstance(key, tuple):
660663
with suppress(IndexingError):
664+
# suppress "Too many indexers"
661665
return self._convert_tuple(key)
662666

663667
if isinstance(key, range):
664668
return list(key)
665669

666670
return self._convert_to_indexer(key, axis=0)
667671

672+
@final
673+
def _tupleize_axis_indexer(self, key) -> tuple:
674+
"""
675+
If we have an axis, adapt the given key to be axis-independent.
676+
"""
677+
new_key = [slice(None)] * self.ndim
678+
# error: Invalid index type "Optional[Any]" for "List[slice]"; expected
679+
# type "SupportsIndex"
680+
new_key[self.axis] = key # type:ignore[index]
681+
return tuple(new_key)
682+
683+
@final
668684
def _ensure_listlike_indexer(self, key, axis=None, value=None):
669685
"""
670686
Ensure that a list-like of column labels are all present by adding them if
@@ -702,6 +718,7 @@ def _ensure_listlike_indexer(self, key, axis=None, value=None):
702718
keys, axis=0, consolidate=False, only_slice=True
703719
)
704720

721+
@final
705722
def __setitem__(self, key, value):
706723
check_deprecated_indexers(key)
707724
if isinstance(key, tuple):
@@ -737,6 +754,7 @@ def _validate_key(self, key, axis: int):
737754
"""
738755
raise AbstractMethodError(self)
739756

757+
@final
740758
def _expand_ellipsis(self, tup: tuple) -> tuple:
741759
"""
742760
If a tuple key includes an Ellipsis, replace it with an appropriate
@@ -758,6 +776,7 @@ def _expand_ellipsis(self, tup: tuple) -> tuple:
758776
# by _validate_key_length
759777
return tup
760778

779+
@final
761780
def _validate_tuple_indexer(self, key: tuple) -> tuple:
762781
"""
763782
Check the key for valid keys across my indexer.
@@ -774,6 +793,7 @@ def _validate_tuple_indexer(self, key: tuple) -> tuple:
774793
) from err
775794
return key
776795

796+
@final
777797
def _is_nested_tuple_indexer(self, tup: tuple) -> bool:
778798
"""
779799
Returns
@@ -784,23 +804,18 @@ def _is_nested_tuple_indexer(self, tup: tuple) -> bool:
784804
return any(is_nested_tuple(tup, ax) for ax in self.obj.axes)
785805
return False
786806

787-
def _convert_tuple(self, key):
807+
@final
808+
def _convert_tuple(self, key: tuple) -> tuple:
809+
# Note: we assume _tupleize_axis_indexer has been called, if necessary.
788810
keyidx = []
789-
if self.axis is not None:
790-
axis = self.obj._get_axis_number(self.axis)
791-
for i in range(self.ndim):
792-
if i == axis:
793-
keyidx.append(self._convert_to_indexer(key, axis=axis))
794-
else:
795-
keyidx.append(slice(None))
796-
else:
797-
self._validate_key_length(key)
798-
for i, k in enumerate(key):
799-
idx = self._convert_to_indexer(k, axis=i)
800-
keyidx.append(idx)
811+
self._validate_key_length(key)
812+
for i, k in enumerate(key):
813+
idx = self._convert_to_indexer(k, axis=i)
814+
keyidx.append(idx)
801815

802816
return tuple(keyidx)
803817

818+
@final
804819
def _validate_key_length(self, key: tuple) -> tuple:
805820
if len(key) > self.ndim:
806821
if key[0] is Ellipsis:
@@ -812,6 +827,7 @@ def _validate_key_length(self, key: tuple) -> tuple:
812827
raise IndexingError("Too many indexers")
813828
return key
814829

830+
@final
815831
def _getitem_tuple_same_dim(self, tup: tuple):
816832
"""
817833
Index with indexers that should return an object of the same dimension
@@ -831,6 +847,7 @@ def _getitem_tuple_same_dim(self, tup: tuple):
831847

832848
return retval
833849

850+
@final
834851
def _getitem_lowerdim(self, tup: tuple):
835852

836853
# we can directly get the axis result since the axis is specified
@@ -892,6 +909,7 @@ def _getitem_lowerdim(self, tup: tuple):
892909

893910
raise IndexingError("not applicable")
894911

912+
@final
895913
def _getitem_nested_tuple(self, tup: tuple):
896914
# we have a nested tuple so have at least 1 multi-index level
897915
# we should be able to match up the dimensionality here
@@ -951,6 +969,7 @@ def _getitem_nested_tuple(self, tup: tuple):
951969
def _convert_to_indexer(self, key, axis: int):
952970
raise AbstractMethodError(self)
953971

972+
@final
954973
def __getitem__(self, key):
955974
check_deprecated_indexers(key)
956975
if type(key) is tuple:
@@ -978,6 +997,7 @@ def _getitem_axis(self, key, axis: int):
978997
def _has_valid_setitem_indexer(self, indexer) -> bool:
979998
raise AbstractMethodError(self)
980999

1000+
@final
9811001
def _getbool_axis(self, key, axis: int):
9821002
# caller is responsible for ensuring non-None axis
9831003
labels = self.obj._get_axis(axis)
@@ -1544,7 +1564,7 @@ def _get_setitem_indexer(self, key):
15441564
key = list(key)
15451565

15461566
if self.axis is not None:
1547-
return self._convert_tuple(key)
1567+
key = self._tupleize_axis_indexer(key)
15481568

15491569
return key
15501570

0 commit comments

Comments
 (0)