Repository: modin-project/modin

Issue:
STY: indexers should be immutable
e.g. _LocationIndexerBase shouldn't be altered outside of `__init__`


Discussion:

Resolving patch:
diff --git a/modin/pandas/indexing.py b/modin/pandas/indexing.py
--- a/modin/pandas/indexing.py
+++ b/modin/pandas/indexing.py
@@ -281,12 +281,54 @@ class _LocationIndexerBase(ClassLogger):
     def __init__(self, modin_df):
         self.df = modin_df
         self.qc = modin_df._query_compiler
-        self.row_scalar = False
-        self.col_scalar = False
-        self.row_multiindex_full_lookup = False
-        self.col_multiindex_full_lookup = False
 
-    def __getitem__(self, row_lookup, col_lookup, ndim):
+    def __getitem__(self, key):  # pragma: no cover
+        """
+        Retrieve dataset according to `key`.
+
+        Parameters
+        ----------
+        key : callable, scalar, or tuple
+            The global row index to retrieve data from.
+
+        Returns
+        -------
+        modin.pandas.DataFrame or modin.pandas.Series
+            Located dataset.
+
+        See Also
+        --------
+        pandas.DataFrame.loc
+        """
+        raise NotImplementedError("Implemented by subclasses")
+
+    def __setitem__(self, key, item):  # pragma: no cover
+        """
+        Assign `item` value to dataset located by `key`.
+
+        Parameters
+        ----------
+        key : callable or tuple
+            The global row numbers to assign data to.
+        item : modin.pandas.DataFrame, modin.pandas.Series or scalar
+            Value that should be assigned to located dataset.
+
+        See Also
+        --------
+        pandas.DataFrame.iloc
+        """
+        raise NotImplementedError("Implemented by subclasses")
+
+    def _getitem_positional(
+        self,
+        row_lookup,
+        col_lookup,
+        row_multiindex_full_lookup: bool,
+        col_multiindex_full_lookup: bool,
+        row_scalar: bool,
+        col_scalar: bool,
+        ndim: int,
+    ):
         """
         Retrieve dataset according to `row_lookup` and `col_lookup`.
 
@@ -296,6 +338,14 @@ def __getitem__(self, row_lookup, col_lookup, ndim):
             The global row index to retrieve data from.
         col_lookup : slice(None), range or np.ndarray
             The global col index to retrieve data from.
+        row_multiindex_full_lookup : bool
+            See _multiindex_possibly_contains_key.__doc__.
+        col_multiindex_full_lookup : bool
+            See _multiindex_possibly_contains_key.__doc__.
+        row_scalar : bool
+            Whether indexer for rows is scalar or not.
+        col_scalar : bool
+            Whether indexer for columns is scalar or not.
         ndim : {0, 1, 2}
             Number of dimensions in dataset to be retrieved.
 
@@ -323,11 +373,14 @@ def __getitem__(self, row_lookup, col_lookup, ndim):
                 extra_log=f"Only None-slices are acceptable as a slice argument in masking, got: {col_lookup}",
             )
             col_lookup = None
+
         qc_view = self.qc.view(row_lookup, col_lookup)
+
         if ndim == 2:
             return self.df.__constructor__(query_compiler=qc_view)
-        if isinstance(self.df, Series) and not self.row_scalar:
+        if isinstance(self.df, Series) and not row_scalar:
             return self.df.__constructor__(query_compiler=qc_view)
+
         if isinstance(self.df, Series):
             axis = 0
         elif ndim == 0:
@@ -340,15 +393,17 @@ def __getitem__(self, row_lookup, col_lookup, ndim):
             # on the row or column index.
             axis = (
                 None
-                if (self.col_scalar and self.row_scalar)
-                or (self.row_multiindex_full_lookup and self.col_multiindex_full_lookup)
+                if (col_scalar and row_scalar)
+                or (row_multiindex_full_lookup and col_multiindex_full_lookup)
                 else 1
-                if self.col_scalar or self.col_multiindex_full_lookup
+                if col_scalar or col_multiindex_full_lookup
                 else 0
             )
-        return self.df.__constructor__(query_compiler=qc_view).squeeze(axis=axis)
 
-    def __setitem__(self, row_lookup, col_lookup, item, axis=None):
+        res_df = self.df.__constructor__(query_compiler=qc_view)
+        return res_df.squeeze(axis=axis)
+
+    def _setitem_positional(self, row_lookup, col_lookup, item, axis=None):
         """
         Assign `item` value to located dataset.
 
@@ -601,8 +656,8 @@ def __getitem__(self, key):
         if self.df.empty:
             return self.df._default_to_pandas(lambda df: df.loc[key])
         row_loc, col_loc, ndim = self._parse_row_and_column_locators(key)
-        self.row_scalar = is_scalar(row_loc)
-        self.col_scalar = is_scalar(col_loc)
+        row_scalar = is_scalar(row_loc)
+        col_scalar = is_scalar(col_loc)
 
         # The thought process here is that we should check to see that we have a full key lookup
         # for a MultiIndex DataFrame. If that's the case, then we should not drop any levels
@@ -610,26 +665,37 @@ def __getitem__(self, key):
         # Thus, we need to make sure we don't try to drop these levels again. The logic here is
         # kind of hacked together. Ideally, we should handle this properly in the lower-level
         # implementations, but this will have to be engineered properly later.
-        self.row_multiindex_full_lookup = self._multiindex_possibly_contains_key(
+        row_multiindex_full_lookup = self._multiindex_possibly_contains_key(
             axis=0, key=row_loc
         )
-        self.col_multiindex_full_lookup = self._multiindex_possibly_contains_key(
+        col_multiindex_full_lookup = self._multiindex_possibly_contains_key(
             axis=1, key=col_loc
         )
         levels_already_dropped = (
-            self.row_multiindex_full_lookup or self.col_multiindex_full_lookup
+            row_multiindex_full_lookup or col_multiindex_full_lookup
         )
 
         if isinstance(row_loc, Series) and is_boolean_array(row_loc):
             return self._handle_boolean_masking(row_loc, col_loc)
 
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
-        result = super(_LocIndexer, self).__getitem__(row_lookup, col_lookup, ndim)
+
+        result = self._getitem_positional(
+            row_lookup,
+            col_lookup,
+            row_multiindex_full_lookup,
+            col_multiindex_full_lookup,
+            row_scalar,
+            col_scalar,
+            ndim,
+        )
+
         if isinstance(result, Series):
             result._parent = self.df
             result._parent_axis = 0
-        col_loc_as_list = [col_loc] if self.col_scalar else col_loc
-        row_loc_as_list = [row_loc] if self.row_scalar else row_loc
+
+        col_loc_as_list = [col_loc] if col_scalar else col_loc
+        row_loc_as_list = [row_loc] if row_scalar else row_loc
         # Pandas drops the levels that are in the `loc`, so we have to as well.
         if (
             isinstance(result, (Series, DataFrame))
@@ -716,7 +782,7 @@ def _loc(df):
             self.qc = self.df._query_compiler
         else:
             row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
-            super(_LocIndexer, self).__setitem__(
+            self._setitem_positional(
                 row_lookup,
                 col_lookup,
                 item,
@@ -882,8 +948,8 @@ def __getitem__(self, key):
         if self.df.empty:
             return self.df._default_to_pandas(lambda df: df.iloc[key])
         row_loc, col_loc, ndim = self._parse_row_and_column_locators(key)
-        self.row_scalar = is_scalar(row_loc)
-        self.col_scalar = is_scalar(col_loc)
+        row_scalar = is_scalar(row_loc)
+        col_scalar = is_scalar(col_loc)
         self._check_dtypes(row_loc)
         self._check_dtypes(col_loc)
 
@@ -891,7 +957,17 @@ def __getitem__(self, key):
             return self._handle_boolean_masking(row_loc, col_loc)
 
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
-        result = super(_iLocIndexer, self).__getitem__(row_lookup, col_lookup, ndim)
+
+        result = self._getitem_positional(
+            row_lookup,
+            col_lookup,
+            row_multiindex_full_lookup=False,
+            col_multiindex_full_lookup=False,
+            row_scalar=row_scalar,
+            col_scalar=col_scalar,
+            ndim=ndim,
+        )
+
         if isinstance(result, Series):
             result._parent = self.df
             result._parent_axis = 0
@@ -929,7 +1005,7 @@ def _iloc(df):
         self._check_dtypes(col_loc)
 
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
-        super(_iLocIndexer, self).__setitem__(
+        self._setitem_positional(
             row_lookup,
             col_lookup,
             item,


Test patch: