当切片索引超出范围时,如何引发IndexError?

ele*_*ent 6 python indexing exception slice

Python文档指出,

切片索引被静默截断以落在允许的范围内

因此IndexErrors,无论使用什么startstop参数,切片列表时都没有上升:

>>> egg = [1, "foo", list()]
>>> egg[5:10]
[]
Run Code Online (Sandbox Code Playgroud)

由于列表egg中不包含任何大于的索引2,因此a egg[5]egg[10]call会引发IndexError:

>> egg[5]
Traceback (most recent call last):
IndexError: list index out of range
Run Code Online (Sandbox Code Playgroud)

现在的问题是,当给定的切片指数超出范围时,我们如何提出IndexError

Mar*_*ers 3

这里没有灵丹妙药。你必须测试两个边界:

def slice_out_of_bounds(sequence, start=None, stop=None, step=1):
    length = len(sequence)
    if start is None:  # default depends on step direction
        start = length - 1 if step < 0 else 0
    elif start < 0:  # relative to length
        start += length
    if stop is None:  # default depends on step direction
        stop = -1 if step < 0 else length
    elif stop < 0:  # relative to length
        stop += length
    # stop can range [0 .. length] for positive steps or
    # [-1 .. (length - 1)] for negative steps (both bounds inclusive).
    # adjust stop for negative steps to make the bounds check easier.
    if step < 0:
        stop += 1
    if not (0 <= start < length and 0 <= stop <= length):
        raise IndexError("Container slice indices out of bounds")
Run Code Online (Sandbox Code Playgroud)

注意我们需要特殊处理的方式stop。切片中的最终值是独占的,因此允许范围最大为length。对于负步长,有效值stop位于范围内[-1 .. length)(或[-1 .. (length - 1)]包含边界),但向该stop值添加 1 可以重复使用相同的0 <= stop <= length测试。

__getitem__()如果您使用方法创建自己的容器类,则对容器进行切片会为您提供一个slice()实例。在这种情况下,不要使用该slice.indices()方法,它会为您调整边界,而不是引发IndexError. 相反,我们使用.start,.stop.step属性。其中每一个都可以是None,包括.step,所以也请确保处理好它。

这是__getitem__上述的一个版本,添加了一些针对其他边缘情况的检查,以复制list切片的工作原理:

class SomeContainer:
    ...

    def __getitem__(self, key):
        length = len(self)

        if isinstance(key, int) and not (0 <= key < length):
            raise IndexError("Container index out of range")

        elif isinstance(key, slice):
            if key.step == 0:
                raise ValueError("Slice step cannot be zero")
            start, stop, step = key.start, key.stop, key.step or 1
            if start is None:  # default depends on step direction
                start = length - 1 if step < 0 else 0
            elif start < 0:  # relative to length
                start += length
            if end is None:  # default depends on step direction
                end = -1 if step < 0 else length
            elif end < 0:  # relative to length
                end += length
            # end can range [0 .. length] for positive steps or
            # [-1 .. (length - 1)] for negative steps (both bounds inclusive).
            # adjust end for negitive steps to make the bounds check easier.
            # Don't do this if you also wanted to calculate the slice length!
            if step < 0:
                end += 1
            if not (0 <= start < length and 0 <= end <= length):
                raise IndexError("Container slice indices out of bounds")

         else:
            raise TypeError(f"list indices must be integers or slices, not {type(key)}")


Run Code Online (Sandbox Code Playgroud)