代码之家  ›  专栏  ›  技术社区  ›  Akaisteph7

为什么python允许序列的切片索引超出范围?

  •  54
  • Akaisteph7  · 技术社区  · 6 年前

    所以我刚刚遇到了一个奇怪的python特性,我想澄清一下。

    下面的数组操作有些意义:

    p = [1,2,3]
    p[3:] = [4] 
    p = [1,2,3,4]
    

    我想它实际上只是在末尾附加这个值,对吗?
    但是,我为什么要这样做?

    p[20:22] = [5,6]
    p = [1,2,3,4,5,6]
    

    更重要的是:

    p[20:100] = [7,8]
    p = [1,2,3,4,5,6,7,8]
    

    这似乎是错误的逻辑。似乎这会引发一个错误!

    有什么解释吗?
    -这只是巨蟒做的一件奇怪的事吗?
    -有什么目的吗?
    -或者我是不是想错了?

    2 回复  |  直到 6 年前
        1
  •  62
  •   iacob    6 年前

    关于超范围指数的部分问题

    切片逻辑自动将索引剪辑到序列的长度。

    为了方便起见,允许切片索引扩展到超过端点。必须对每个表达式进行范围检查,然后手动调整限制,这将是一件很痛苦的事情,所以Python会为您这样做。

    考虑希望显示不超过文本消息前50个字符的用例。

    简单的方法(Python现在做的事情):

    preview = msg[:50]
    

    或者困难的方法(自己做极限检查):

    n = len(msg)
    preview = msg[:50] if n > 50 else msg
    

    手动实现端点调整的逻辑很容易被忘记,也很容易出错(在两个地方更新50),很冗长,而且速度很慢。Python将这种逻辑移动到其内部,使其简洁、自动、快速和正确。这就是我喜欢蟒蛇的原因之一——)

    关于分配长度与输入长度不匹配的部分问题

    OP还想知道允许诸如 p[20:100] = [7,8] 其中,分配目标的长度(80)与替换数据的长度(2)不同。

    用弦作类比最容易看出动机。考虑一下, "five little monkeys".replace("little", "humongous") . 注意目标“小”只有六个字母,“大”有九个字母。我们对列表也可以这样做:

    >>> s = list("five little monkeys")
    >>> i = s.index('l')
    >>> n = len('little')
    >>> s[i : i+n ] = list("humongous")
    >>> ''.join(s)
    'five humongous monkeys'
    

    这一切归根结底都是为了方便。

    在引入 复制() 清除() 方法,这些习惯用语:

    s[:] = []           # clear a list
    t = u[:]            # copy a list
    

    即使现在,我们在筛选时也会使用此项更新列表:

    s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values
    

    希望这些实际的例子能给我们一个很好的视角来解释为什么切片可以像它那样工作。

        2
  •  20
  •   iz_    6 年前

    这个 documentation 有你的答案:

    s[i:j] 切片 s i j (注释(4))

    (4)切片 S J 定义为项目序列 带索引 k 这样 i <= k < j . 如果 J 大于 len(s) 使用 莱恩(S) . 如果 被省略或 None 使用 0 . 如果 J 被省略或 没有 使用 莱恩(S) . 如果 大于或等于 J ,切片为空。

    这个 documentation of IndexError 确认此行为:

    例外 索引错误

    序列下标超出范围时引发。( 切片索引被静默截断,以落在允许的范围内; 如果索引是 不是整数, TypeError 上升。)

    基本上,像 p[20:100] 正在减少到 p[len(p):len(p] . P[LeN(p):LeN(P]) 是列表末尾的一个空切片,将列表分配给它将修改列表末尾以包含所述列表。因此,它的工作方式类似于附加/扩展原始列表。

    此行为与将列表分配给空切片时所发生的行为相同。 在任何地方 在原始列表中。例如:

    In [1]: p = [1, 2, 3, 4]
    
    In [2]: p[2:2] = [42, 42, 42]
    
    In [3]: p
    Out[3]: [1, 2, 42, 42, 42, 3, 4]