This function is called when key is a slice, or an integer,
        or a tuple of slices or integers

def _get_nd_basic_indexing(self, key):
        """This function is called when key is a slice, or an integer,
        or a tuple of slices or integers"""
        shape = self.shape
        if isinstance(key, integer_types):
            if key > shape[0] - 1:
                raise IndexError(
                    'index {} is out of bounds for axis 0 with size {}'.format(
                        key, shape[0]))
            return self._at(key)
        elif isinstance(key, py_slice):
            if key.step is not None and key.step != 1:
                if key.step == 0:
                    raise ValueError("slice step cannot be zero")
                return op.slice(self, begin=(key.start,), end=(key.stop,), step=(key.step,))
            elif key.start is not None or key.stop is not None:
                return self._slice(key.start, key.stop)
            else:
                return self

        if not isinstance(key, tuple):
            raise ValueError('index=%s must be a slice, or an ineger, or a tuple'
                             ' of slices and integers to use basic indexing, received type=%s'
                             % (str(key), str(type(key))))
        assert len(key) != 0, 'basic index cannot be an empty tuple'
        begin = []
        end = []
        step = []
        kept_axes = []  # axes where slice_i is a slice
        i = -1
        for i, slice_i in enumerate(key):
            if isinstance(slice_i, integer_types):
                begin.append(slice_i)
                end.append(slice_i+1 if slice_i != -1 else self.shape[i])
                step.append(1)
            elif isinstance(slice_i, py_slice):
                if slice_i.step == 0:
                    raise ValueError('basic index=%s cannot have slice=%s with step = 0'
                                     % (str(key), str(slice_i)))
                begin.append(slice_i.start)
                end.append(slice_i.stop)
                step.append(slice_i.step)
                kept_axes.append(i)
            else:
                raise ValueError('basic_indexing does not support slicing with '
                                 'index=%s of type=%s.' % (str(slice_i), str(type(slice_i))))
        kept_axes.extend(range(i+1, len(shape)))
        sliced_nd = op.slice(self, begin, end, step)
        if len(kept_axes) == len(shape):
            return sliced_nd
        # squeeze sliced_shape to remove the axes indexed by integers
        oshape = []
        sliced_shape = sliced_nd.shape
        for axis in kept_axes:
            oshape.append(sliced_shape[axis])
        # if key is a tuple of integers, still need to keep 1 dim
        # while in Numpy, the output will become an value instead of an ndarray
        if len(oshape) == 0:
            oshape.append(1)
        oshape = tuple(oshape)
        assert np.prod(oshape) == np.prod(sliced_shape), 'oshape=%s has different size'\
                                                         ' than sliced_shape=%s'\
                                                         % (oshape, sliced_shape)
        return sliced_nd.reshape(oshape)