# Buggy code
def _getitem_lowerdim(self, tup):
    # we can directly get the axis result since the axis is specified
    if self.axis is not None:
        axis = self.obj._get_axis_number(self.axis)
        return self._getitem_axis(tup, axis=axis)

    # we may have a nested tuples indexer here
    if self._is_nested_tuple_indexer(tup):
        return self._getitem_nested_tuple(tup)

    # we maybe be using a tuple to represent multiple dimensions here
    ax0 = self.obj._get_axis(0)
    if isinstance(ax0, MultiIndex):
        result = self._handle_lowerdim_multi_index_axis0(tup)
        if result is not None:
            return result

    if len(tup) > self.obj.ndim:
        raise IndexingError("Too many indexers. handle elsewhere")

    # to avoid wasted computation
    # df.ix[d1:d2, 0] -> columns first (True)
    # df.ix[0, ['C', 'B', A']] -> rows first (False)
    for i, key in enumerate(tup):
        if is_label_like(key) or isinstance(key, tuple):
            section = self._getitem_axis(key, axis=i)

            # we have yielded a scalar ?
            if not is_list_like_indexer(section):
                return section

            elif section.ndim == self.ndim:
                # we're in the middle of slicing through a MultiIndex
                # revise the key wrt to `section` by inserting an _NS
                new_key = tup[:i] + (_NS,) + tup[i + 1 :]

            else:
                new_key = tup[:i] + tup[i + 1 :]

                # unfortunately need an odious kludge here because of
                # DataFrame transposing convention
                if isinstance(section, ABCDataFrame) and i > 0 and len(new_key) == 2:
                    a, b = new_key
                    new_key = b, a

                if len(new_key) == 1:
                    (new_key,) = new_key

            # This is an elided recursive call to iloc/loc/etc'
            return getattr(section, self.name)[new_key]

    raise IndexingError("not applicable")


# Traceback (AttributeError)
df1.iloc[0,0]
C:\Users\rikuhiro\Anaconda3\envs\pd-check\lib\site-packages\ipykernel\__main__.py:1: PerformanceWarning: indexing past lexsort depth may impact performance.
if __name__ == '__main__':
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-37-884ae2904642> in <module>()
----> 1 df1.iloc[0,0]

C:\Users\rikuhiro\Anaconda3\envs\pd-check\lib\site-packages\pandas\core\indexing.py in __getitem__(self, key)
1292
1293         if type(key) is tuple:
-> 1294             return self._getitem_tuple(key)
1295         else:
1296             return self._getitem_axis(key, axis=0)

C:\Users\rikuhiro\Anaconda3\envs\pd-check\lib\site-packages\pandas\core\indexing.py in _getitem_tuple(self, tup)
1561
1562             # if the dim was reduced, then pass a lower-dim the next time
-> 1563             if retval.ndim < self.ndim:
1564                 axis -= 1
1565

AttributeError: 'str' object has no attribute 'ndim'

# Fixed code
def _getitem_lowerdim(self, tup):
    # we can directly get the axis result since the axis is specified
    if self.axis is not None:
        axis = self.obj._get_axis_number(self.axis)
        return self._getitem_axis(tup, axis=axis)

    # we may have a nested tuples indexer here
    if self._is_nested_tuple_indexer(tup):
        return self._getitem_nested_tuple(tup)

    # we maybe be using a tuple to represent multiple dimensions here
    ax0 = self.obj._get_axis(0)
    # ...but iloc should handle the tuple as simple integer-location
    # instead of checking it as multiindex representation (GH 13797)
    if isinstance(ax0, MultiIndex) and self.name != "iloc":
        result = self._handle_lowerdim_multi_index_axis0(tup)
        if result is not None:
            return result

    if len(tup) > self.obj.ndim:
        raise IndexingError("Too many indexers. handle elsewhere")

    # to avoid wasted computation
    # df.ix[d1:d2, 0] -> columns first (True)
    # df.ix[0, ['C', 'B', A']] -> rows first (False)
    for i, key in enumerate(tup):
        if is_label_like(key) or isinstance(key, tuple):
            section = self._getitem_axis(key, axis=i)

            # we have yielded a scalar ?
            if not is_list_like_indexer(section):
                return section

            elif section.ndim == self.ndim:
                # we're in the middle of slicing through a MultiIndex
                # revise the key wrt to `section` by inserting an _NS
                new_key = tup[:i] + (_NS,) + tup[i + 1 :]

            else:
                new_key = tup[:i] + tup[i + 1 :]

                # unfortunately need an odious kludge here because of
                # DataFrame transposing convention
                if isinstance(section, ABCDataFrame) and i > 0 and len(new_key) == 2:
                    a, b = new_key
                    new_key = b, a

                if len(new_key) == 1:
                    (new_key,) = new_key

            # This is an elided recursive call to iloc/loc/etc'
            return getattr(section, self.name)[new_key]

    raise IndexingError("not applicable")