题 理解Python的切片表示法


我需要在Python的切片表示法上有一个很好的解释(引用是一个加号)。

对我而言,这种符号需要一点点提升。

它看起来非常强大,但我还没有完全理解它。


2300
2018-02-03 22:31


起源




答案:


它非常简单:

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

还有 step 值,可以与上述任何一个一起使用:

a[start:end:step] # start through not past end, by step

要记住的关键点是 :end value表示第一个值  在选定的切片中。所以,两者之间的区别 end 和 start 是选择的元素数量(如果 step 是1,默认值)。

另一个特点是 start 要么 end 可能是一个  数字,这意味着它从数组的末尾而不是从开头算起。所以:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

同样的, step 可能是负数:

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

如果项目少于您的要求,Python对程序员很友好。例如,如果你要求 a[:-2] 和 a 只包含一个元素,您将得到一个空列表而不是错误。有时您会更喜欢错误,因此您必须意识到这可能会发生。


3113
2018-02-03 22:48



切片内置类型会返回一个副本,但这不是通用的。值得注意的是, 切片NumPy数组 返回与原始内容共享内存的视图。 - Beni Cherniavsky-Paskin
怎么会这样? [::-2]  我试过了,但我不明白。例如,使用此列表 a = ['1', '2', '3', '4', '5'] 。 - RodriKing


Python教程 谈论它(向下滚动一下,直到你得到关于切片的部分)。

ASCII艺术图也有助于记住切片的工作方式:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

记住切片如何工作的一种方法是将索引视为指向 之间 字符,第一个字符的左边缘编号为0.然后是字符串的最后一个字符的右边缘 ñ 字符有索引 ñ


396
2018-02-03 22:49



对于具有否定步骤的切片,我发现这种ASCII艺术令人困惑,它应该扩展到 -6 以来 'ApleH'[:-6:-1] 是一个有效的切片,与使用不同 -5 - Chris_Rands
“记住切片如何工作的一种方法是将指数视为指向字符之间” - 这是一种思考它的好方法 - jusopi


列举语法允许的可能性:

>>> seq[:]                # [seq[0],   seq[1],          ..., seq[-1]    ]
>>> seq[low:]             # [seq[low], seq[low+1],      ..., seq[-1]    ]
>>> seq[:high]            # [seq[0],   seq[1],          ..., seq[high-1]]
>>> seq[low:high]         # [seq[low], seq[low+1],      ..., seq[high-1]]
>>> seq[::stride]         # [seq[0],   seq[stride],     ..., seq[-1]    ]
>>> seq[low::stride]      # [seq[low], seq[low+stride], ..., seq[-1]    ]
>>> seq[:high:stride]     # [seq[0],   seq[stride],     ..., seq[high-1]]
>>> seq[low:high:stride]  # [seq[low], seq[low+stride], ..., seq[high-1]]

当然,如果 (high-low)%stride != 0,那么终点将略低于 high-1

如果 stride 是否定的,因为我们倒计时,顺序有所改变:

>>> seq[::-stride]        # [seq[-1],   seq[-1-stride],   ..., seq[0]    ]
>>> seq[high::-stride]    # [seq[high], seq[high-stride], ..., seq[0]    ]
>>> seq[:low:-stride]     # [seq[-1],   seq[-1-stride],   ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]

扩展切片(带逗号和省略号)主要仅由特殊数据结构(如Numpy)使用;基本序列不支持它们。

>>> class slicee:
...     def __getitem__(self, item):
...         return `item`
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

310
2018-02-03 23:08



如果发生了奇怪的事情 stride 是负面的,如 [::-1]? - Charlie Parker
@CharlieParker当 strike 是消极的,倒计时 high 至 low。 - ephemient
实际上仍然存在一些问题,例如如果我键入'apple'[4:-4:-1]我得到'elp',python将-4翻译为1? - liyuan


上面的答案不讨论切片分配:

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

这也可以澄清切片和索引之间的区别。


199
2018-01-18 21:37



你能解释第二个吗? - chandresh


解释Python的切片表示法

简而言之,冒号(:)下标符号(subscriptable[subscriptarg])制作切片符号 - 具有可选参数, startstopstep

sliceable[start:stop:step]

Python切片是一种有条不紊地访问部分数据的计算速度快的方法。在我看来,即使是一个中级Python程序员,它也是必须熟悉的语言的一个方面。

重要定义

首先,让我们定义一些术语:

开始: 切片的起始索引,它将包括此索引处的元素,除非它与之相同 停止,默认为0,即第一个索引。如果它是否定的,则意味着开始 n 从最后的项目。

停止: 切片的结束索引,确实如此  包含此索引处的元素,默认为要切片的序列的长度,即直到并包括结尾。

步: 索引增加的数量,默认为1.如果它是负数,则表示您正在反向切片。

索引如何工作

您可以制作任何正数或负数。正数的含义很简单,但对于负数,就像Python中的索引一样,你从最后的数字向后计算 开始 和 停止,为了 ,你只需减少索引。这个例子是 来自文档的教程,但我稍微修改了它,以指示每个索引引用的序列中的哪个项目:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

切片如何工作

要使用带有支持它的序列的切片表示法,必须在序列后面的方括号中包含至少一个冒号(实际上 实施 __getitem__ 根据Python数据模型的序列方法。)

切片表示法的工作方式如下:

sequence[start:stop:step]

并回想一下,有默认值 开始停止,和 ,所以要访问默认值,只需省略参数。

从列表中获取最后九个元素的切片表示法(或任何其他支持它的序列,如字符串)将如下所示:

my_list[-9:]

当我看到这个时,我将括号中的部分读作“从结尾到结尾的第9个”。 (实际上,我在精神上将其缩写为“-9,on”)

说明:

完整的符号是

my_list[-9:None:None]

并替换默认值(实际上是 step 是消极的, stop默认是 -len(my_list) - 1所以 None 对于停止真的只是意味着它去任何结束步骤():

my_list[-9:len(my_list):1]

结肠:是什么告诉Python你给它一个切片而不是常规索引。这就是为什么在Python 2中制作浅表列表的惯用方法是

list_copy = sequence[:]

清除它们是:

del my_list[:]

(Python 3获得了一个 list.copy 和 list.clear 方法。)

什么时候 step 是负数,默认值为 start 和 stop 更改

默认情况下,当 step 参数是空的(或 None),它被分配给 +1

但是你可以传入一个负整数,并且列表(或大多数其他标准的可切片)将从末尾切换到开头。

因此,负片会更改默认值 start 和 stop

在源中确认这一点

我想鼓励用户阅读源代码和文档。该 切片对象的源代码和此逻辑可在此处找到。首先,我们确定是否 step 是否定的:

 step_is_negative = step_sign < 0;

如果是这样,下限是 -1  意思是我们一直切到包括开头,上限是长度减去1,这意味着我们从最后开始。 (注意这个的语义 -1 是 不同 从一个 -1 用户可以在Python中传递指示最后一项的索引。)

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

除此以外 step 是正数,下限将为零和上限(我们上升但不包括)切片列表的长度。

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

然后,我们可能需要应用默认值 start 和 stop  - 默认然后为 start 计算为上限时 step 是否定的:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

stop,下限:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

给你的切片一个描述性的名字!

您可能会发现将切片与传递切片分开是很有用的 list.__getitem__ 方法 (这就是方括号的作用)。即使您不是新手,它也会使您的代码更具可读性,以便其他可能必须阅读您的代码的人可以更容易地理解您正在做的事情。

但是,您不能只将一些以冒号分隔的整数分配给变量。您需要使用切片对象:

last_nine_slice = slice(-9, None)

第二个论点, None,是必需的,所以第一个参数被解释为 start 论据 否则就是了 stop 论据

然后,您可以将切片对象传递给序列:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

有趣的是,范围也需要切片:

>>> range(100)[last_nine_slice]
range(91, 100)

内存注意事项:

由于Python列表的切片在内存中创建了新对象,因此需要注意的另一个重要功能是 itertools.islice。通常,您需要迭代切片,而不是仅在内存中静态创建。 islice 这是完美的。需要注意的是,它不支持负面论据 startstop, 要么 step,如果这是一个问题,您可能需要提前计算指数或反转可迭代。

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

现在:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

列表切片复制的事实是列表本身的一个特征。如果您正在切割像Pandas DataFrame这样的高级对象,它可能会返回原始视图,而不是副本。


186
2017-07-12 13:19



嗨@aaronhall,优秀的帖子!你会考虑将这篇文章贡献给中级Python书 - github.com/yasoob/intermediatePython/issues/153 ? - neowulf33
我只是想对这篇文章说声谢谢,我已经写了一段时间了,现在仍然回到这里,因为我一直在用切片做出一个错误! - akkatracker
这里最好的答案 - kskyriacou


当我第一次看到切片语法时,有些事情对我来说并不是很明显:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

简单的方法来反转序列!

如果您出于某种原因想要按相反顺序中的每个第二项:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]

124
2018-02-03 23:15





找到了这张伟大的餐桌 http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

84
2017-09-06 06:50





在Python 2.7中

在Python中切片

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

理解索引分配非常重要。

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

当你说[a:b:c]时,你要说的是取决于c(向前或向后)的符号,从a开始到b结束(不包括bth索引处的元素)。使用上面的索引规则并记住您只能找到此范围内的元素:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

但是这个范围在两个方向上无限延续:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

例如:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

如果你选择a,b和c允许与上面a,b,c的规则遍历时的上述范围重叠,你将获得一个包含元素的列表(在遍历期间触摸)或者你将获得一个空列表。

最后一件事:如果a和b相等,那么你也得到一个空列表:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]

84
2017-10-22 05:33



另一个有趣的例子: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2] 结果是 [9] - Deviacium


稍微使用它之后,我意识到最简单的描述是它与for循环中的参数完全相同...

(from:to:step)

其中任何一个都是可选的

(:to:step)
(from::step)
(from:to)

那么负索引只需要你将字符串的长度添加到负索引来理解它。

无论如何这对我有用......


47
2018-02-19 20:52