繁体   English   中英

如何用切片符号a [:: - 1]来解释序列的反转

[英]How to explain the reverse of a sequence by slice notation a[::-1]

来自python.org教程

切片索引具有有用的默认值; 省略的第一个索引默认为零,省略的第二个索引默认为要切片的字符串的大小。

>>> a = "hello"
>>> print(a[::-1])
olleh

正如教程所说a[::-1]应该等于a[0:5:-1]

但是a[0:5:-1]是空的,如下所示:

>>> print(len(a[0:5:-1]))
0

问题不是解释切片符号的重复。 那个问题是关于python中切片的一般用法。

我认为文档可能对此有点误导,但是如果省略切片的可选参数与使用None相同:

>>> a = "hello"
>>> a[::-1]
'olleh'
>>> a[None:None:-1]
'olleh'

您可以看到这两个上面的切片与CPython字节码完全相同:

>>> import dis
>>> dis.dis('a[::-1]') # or dis.dis('a[None:None:-1]')
  1           0 LOAD_NAME                0 (a)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 LOAD_CONST               2 (-1)
             12 BUILD_SLICE              3
             15 BINARY_SUBSCR
             16 RETURN_VALUE

对于否定stepNone的替换值为len(a) - 1表示start-len(a) - 1表示end

>>> a[len(a)-1:-len(a)-1:-1]
'olleh'
>>> a[4:-6:-1]
'olleh'
>>> a[-1:-6:-1]
'olleh'

这可以帮助您将其可视化:

    h  e  l  l  o   
    0  1  2  3  4  5
-6 -5 -4 -3 -2 -1

它只是切片 你选。 开始停止和步骤所以基本上你说它应该从头开始直到开始但向后(-1)。

如果你用-2做它会跳过字母:

>>> a[::-2]
'olh'

在做[0:5:-1]你从第一个字母开始直接回到5,因此它会停止。 只有当你尝试[-1::-1]它才能正确地通过做负1的步骤到达开头。

编辑以回答评论

正如文件所指出的那样

省略的第二个索引默认为要切片的字符串的大小。

让我们假设我们有strlen(str) = 5 当你对字符串进行切片并省略时,请忽略,第二个数字默认为被切片的字符串的长度,在这种情况下 - 5.即str[1:] == str[1:5]str[2:] == str[2:5] 句子指的是原始对象的长度而不是新切片的对象。

此外, 这个答案很棒

你对步进的行为感到困惑。 要获得相同的结果,您可以做的是:

a[0:5][::-1]
'olleh'

事实上,步进想要在你的情况下向后“绕圈”,但是你通过调用a[0:5:-1]来限制它的运动。

a[0:5:-1]没有多大意义,因为当你使用这种表示法时,索引意味着: a[start:end:step] 当您使用负步时,您的结束值需要处于比起始值更“早”的位置。

您会注意到第三个切片参数,即step ,未在您引用的教程部分中显示。 该特定片段采取积极步骤。

当你添加消极步骤的可能性时,行为实际上非常直观。 start参数指的是序列中的任何一端开始以step值指示的方向逐步通过整个序列。 换句话说,如果你有一个正步骤,它指的是最低指数(计数),如果你有一个负步骤,它指的是最高指数(倒计时)。 同样,空end参数指的是在步进通过适当方向后最终结束的序列中的任何一端。

正如您所指出的,文档对于默认值是不正确的。 但是,除了那个小错误之外,它们是一致的。 您可以在此处查看我所指的文档: https//docs.python.org/3/library/stdtypes.html#common-sequence-operations

请注意,根据文档,行为是定义正确的:

具有步骤k的从i到j的s的片被定义为具有索引x = i + n * k的项的序列,使得0 <= n <(ji)/ k。 换句话说,索引是i,i + k,i + 2 * k,i + 3 * k等等,当到达j时停止(但是从不包括j)。

当你这样做时:

>>> a = "hello"
>>> y = a[0:5:-1]

我们有i == 0j == 5k == -1 所以我们抓住索引x = i + n*k ,从0开始到n ,然后上升到(ji)/k 但是,请注意(ji)/k == (5-0)/-1 == -5 没有n这样0 <= n < -5 ,所以你得到空字符串:

>>> y
''

a[start:stop][::step]疑问的时候做a[start:stop][::step] (这几乎总是我们想要的)

几乎总是这样的情况,当你将一个消极的步骤传递给x[start:stop:step]东西时,你想要发生的是先选择子选择,然后step倒退(即我们通常想要的) x[start:stop][::step]

此外,为了增加混乱,恰好是这种情况

x[start:stop:step] == x[start:stop][::step] 

如果step > 0 例如:

>>> x = list(range(10))
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[2:6:2]
[2, 4]
>>> x[2:6][::2]
[2, 4]
>>> x[1:10][::3]
[1, 4, 7]
>>> x[1:10:3]
[1, 4, 7]

不幸的是,当step < 0时,这不成立,即使它认为它应该是诱人的。

在被这几次烧毁之后,我意识到执行start:stop slice 之后总是执行step子句会更安全。 所以我几乎总是y = x[start:stop][::step] ,至少在原型设计或创建一个正确性/可读性是原始问题的新模块时。 这比执行单个切片的性能要差,但如果性能是一个问题,那么您可以做的可读性较低:

y = x[start:stop:step] if step > 0 else x[stop:start:step]

HTH。

对于序列[start:stop:step]的 Python切片,已经导出了以下规则:

  1. start:stop = start:stop:1
  2. start:stop:(+或 - )step - 表示遍历跳过序列中的N个项目。 但是,( - )表示向后遍历
  3. 请记住,序列中最后一项的位置为-1,而前一项的位置为-2,依此类推。

#start:stop:+ step规则

  1. 始终向前走
  2. 总是从序列的开始开始,因为它是一个积极的步骤(向前)
  3. 从请求的位置开始,停在请求的位置但不包括项目停止位置
  4. 默认启动:如果未提供启动,则从0开始
  5. 默认停止:如果未提供停止,则表示直到序列结束, 包括最后一个值
  6. 如果无法访问停止位置处的项目(项目超出序列遍历的末尾),则切片不会返回任何内容

#start:stop:-step规则

  1. 始终反向移动
  2. 如果提供了起始位置,则从那里开始,但反向移动(后退一步)
  3. 如果提供停止,则停止在那里移动但排除此项
  4. 默认开始:如果未提供开始位置,则开始位置是序列的最后位置(因为负遍历)
  5. 默认停止:如果未提供停止,则它是列表的开头(位置0)
  6. 如果无法访问停止位置处的项目(项目超出序列遍历的末尾),则切片不会返回任何内容

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM