题
我需要对 Python 的切片表示法有一个很好的解释(参考文献优先)。
对我来说,这个符号需要一些学习。
它看起来非常强大,但我还没有完全理解它。
解决方案
真的很简单:
a[start:stop] # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array
还有 step
值,可以与以上任何一个一起使用:
a[start:stop:step] # start through not past stop, by step
要记住的关键是 :stop
值代表第一个值 不是 在选定的切片中。因此, stop
和 start
是选择的元素数(如果 step
是1,默认值)。
另一个功能是 start
或者 stop
可能是一个 消极的 数字,这意味着它从数组的末端而不是开始。所以:
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
仅包含一个元素,您会得到一个空列表而不是错误。有时您会更喜欢错误,因此您必须意识到这可能会发生。
与 slice()
目的
切片操作员 []
实际上是在上述代码中使用的 slice()
对象使用 :
表示法(仅在 []
), IE:
a[start:stop:step]
等同于:
a[slice(start, stop, step)]
切片对象的行为也略有不同,具体取决于参数的数量,与 range()
, ,即两个 slice(stop)
和 slice(start, stop[, step])
得到支持。跳过指定给定参数,可能会使用 None
, ,这样 a[start:]
等同于 a[slice(start, None)]
或者 a[::-1]
等同于 a[slice(None, None, -1)]
.
而 :
- 基于符号对简单切片非常有用,明确使用 slice()
对象简化了切片的程序化生成。
其他提示
这 Python教程 谈论它(向下滚动一点,直到您到达切片的一部分)。
ASCII艺术图也有助于记住切片的工作方式:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
记住切片的工作方式的一种方法是将指向索引视为指向 之间 字符,第一个字符的左边缘编号为0。然后 n 字符具有索引 n.
枚举语法允许的可能性:
>>> 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 repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
上面的答案不讨论切片分配。要了解切片分配,在ASCII艺术中添加另一个概念很有帮助:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
Slice position: 0 1 2 3 4 5 6
Index position: 0 1 2 3 4 5
>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
'P'
>>> p[5]
'n'
# Slicing gives lists
>>> p[0:1]
['P']
>>> p[0:2]
['P','y']
一种启发式是从零到n的切片,想到:“零是开始,从开始并在列表中获取n个项目”。
>>> p[5] # the last of six items, indexed from zero
'n'
>>> p[0:5] # does NOT include the last item!
['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
['P','y','t','h','o','n']
另一个启发式方法是:“对于任何切片,将起点替换为零,应用以前的启发式词以获得列表的结束,然后计算第一个数字备份以将开始的项目切断。
>>> p[0:4] # Start at the beginning and count out 4 items
['P','y','t','h']
>>> p[1:4] # Take one item off the front
['y','t','h']
>>> p[2:4] # Take two items off the front
['t','h']
# etc.
切片分配的第一个规则是自切片 返回 列表,切片分配 需要 列表(或其他峰值):
>>> p[2:3]
['t']
>>> p[2:3] = ['T']
>>> p
['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
切片分配的第二个规则,您也可以在上面看到,是通过切片索引返回列表的任何部分,这与切片分配更改的相同部分:
>>> p[2:4]
['T','h']
>>> p[2:4] = ['t','r']
>>> p
['P','y','t','r','o','n']
切片分配的第三个规则是,分配的列表(含糊)不必具有相同的长度;索引切片简单地切成薄片,并通过分配的任何内容代替。
>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
['P','y','s','p','a','m','o','n']
习惯的最棘手的部分是分配为空切片。使用启发式1和2,很容易转过头来 索引 一个空切片:
>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]
然后,一旦看到了这一点,切片分配给空切片也很有意义:
>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
['P','y','t','h','x','y','o','n'] # The result is longer still
请注意,由于我们没有更改切片的第二个数字(4),因此即使我们分配给空切片,插入的项目也总是会堆叠在“ O”上。因此,空分配的位置是非空分配的位置的逻辑扩展。
备份一点,当您继续努力计算切片开始时会发生什么?
>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]
>>> p[5:4]
[]
>>> p[6:4]
[]
切成薄片,完成后,就完成了;它不会开始向后切片。在Python中,除非您通过使用负数明确要求它们,否则您不会获得负面步。
>>> p[5:3:-1]
['n','o']
“完成后,完成”规则有一些奇怪的后果:
>>> p[4:4]
[]
>>> p[5:4]
[]
>>> p[6:4]
[]
>>> p[6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
实际上,与索引相比,python切片是奇怪的防误:
>>> p[100:200]
[]
>>> p[int(2e99):int(1e99)]
[]
有时可能会派上用场,但也可能导致某种奇怪的行为:
>>> p
['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']
根据您的申请,可能……或可能不会...成为您希望的东西!
以下是我原始答案的文字。这对许多人很有用,所以我不想删除它。
>>> 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]
这也可以阐明切片和索引之间的差异。
解释Python的切片符号
简而言之:
)在下标符号中(subscriptable[subscriptarg]
)制作切片符号 - 具有可选论点, start
, stop
, step
:
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]
并回想一下有默认 开始, 停止, , 和 步, ,因此要访问默认值,只需省略参数即可。
切片表示法以从列表中获取最后9个元素(或任何其他支持它的序列,如字符串)看起来像这样:
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
非常适合这一点。警告,它不支持负面论据 start
, stop
, , 或者 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之类的高级对象切片,则可能会返回原始视图,而不是副本。
当我第一次看到切片语法时,我的几件事对我来说并不明显:
>>> 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]
在Python 2.7中
切成薄片
[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开始,从a开始(不包括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]
在 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)
使用了一点后,我意识到最简单的描述是它与A中的参数完全相同 for
环形...
(from:to:step)
他们中的任何一个都是可选的:
(:to:step)
(from::step)
(from:to)
然后,否定索引只需要您将字符串的长度添加到负索引以理解它。
无论如何,这对我有用...
我发现要更容易记住它的工作原理,然后我可以找出任何特定的开始/停止/步骤组合。
理解很有启发性 range()
第一的:
def range(start=0, stop, step=1): # Illegal syntax, but that's the effect
i = start
while (i < stop if step > 0 else i > stop):
yield i
i += step
从 start
, ,递增 step
, ,不要到达 stop
. 。很简单。
关于负面步骤的要记住的事情是 stop
无论是更高还是更低,始终是排除的端。如果您想要相反的顺序相同的切片,则分别进行逆转要干净很多:例如 'abcde'[1:-2][::-1]
从左下方切下一个char,从右侧换一个,然后倒转。 (也可以看看 reversed()
.)
序列切片是相同的,只是它首先将负索引归一化,并且永远无法超出序列:
去做: :下面的代码在ABS(步骤)> 1时具有“永不超出序列”的错误;我 思考 我修补了它是正确的,但是很难理解。
def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
if start is None:
start = (0 if step > 0 else len(seq)-1)
elif start < 0:
start += len(seq)
if not 0 <= start < len(seq): # clip if still outside bounds
start = (0 if step > 0 else len(seq)-1)
if stop is None:
stop = (len(seq) if step > 0 else -1) # really -1, not last element
elif stop < 0:
stop += len(seq)
for i in range(start, stop, step):
if 0 <= i < len(seq):
yield seq[i]
不用担心 is None
细节 - 请记住省略 start
和/或 stop
总是做正确的事情来为您提供整个顺序。
正常化的负索引首先允许启动和/或停止从末端独立计数: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc'
尽管 range(1,-2) == []
。有时将归一化视为“长度模拟”,但请注意,它仅添加了一次长度:例如 'abcde'[-53:42]
只是整个字符串。
Index:
------------>
0 1 2 3 4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
0 -4 -3 -2 -1
<------------
Slice:
<---------------|
|--------------->
: 1 2 3 4 :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
: -4 -3 -2 -1 :
|--------------->
<---------------|
我希望这将帮助您在Python中建模列表。
参考: http://wiki.python.org/moin/movingtopythonfromotherlanguages
我使用自己思考的“元素之间的索引点”方法,但是描述它有时可以帮助他人得到的一种方法是:
mylist[X:Y]
X是您想要的第一个元素的索引。
y是您的第一个元素的索引 别 想。
python切片符号:
a[start:end:step]
- 为了
start
和end
, ,负值被解释为相对于序列的末尾。 - 积极指数
end
指示位置 后 最后要包含的元素。 - 空白值默认如下:
[+0:-0:1]
. - 使用负面步骤逆转了对的解释
start
和end
该符号延伸至(numpy)矩阵和多维阵列。例如,要切片整列,您可以使用:
m[::,0:2:] ## slice the first two columns
切片保存数组元素的引用,而不是副本。如果您想将单独的副本制作一个数组,则可以使用 deepcopy()
.
您也可以使用切片分配从列表中删除一个或多个元素:
r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
这就是我向新手教授切片的方式:
理解索引和切片之间的差异:
Wiki Python拥有这张惊人的图片,显然可以区分索引和切片。
它是其中有6个元素的列表。要更好地理解切片,请将该列表视为放置的六个盒子的一组。每个盒子中都有一个字母。
索引就像处理框的内容。您可以检查任何盒子的内容。但是您不能一次检查多个框的内容。您甚至可以替换盒子的内容。但是您不能将2球放在1盒中,也不能一次更换2个球。
In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']
In [124]: alpha[0]
Out[124]: 'a'
In [127]: alpha[0] = 'A'
In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']
In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]
TypeError: list indices must be integers, not tuple
切片就像处理盒子本身。您可以拾取第一个框并将其放在另一个桌子上。要拾取盒子,您需要知道的只是盒子开始和结束的位置。
您甚至可以在1和4之间拾取前3个框或最后2个框或所有盒子。因此,如果知道开始和结束,则可以选择任何集合。该位置称为“开始和停止位置”。
有趣的是,您可以一次更换多个盒子。另外,您可以将多个盒子放在自己喜欢的地方。
In [130]: alpha[0:1]
Out[130]: ['A']
In [131]: alpha[0:1] = 'a'
In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']
In [133]: alpha[0:2] = ['A', 'B']
In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']
In [135]: alpha[2:2] = ['x', 'xx']
In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']
切成步骤:
到现在为止,您一直在挑选盒子。但是有时您需要离散地拾取。例如,您可以每个第二个盒子拾取。您甚至可以从最后的第三个盒子拾取。该值称为步长。这表示您连续的拾音器之间的差距。如果您从头到尾选择框,则步骤大小应为正,反之亦然。
In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']
In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']
In [144]: alpha[1:5:-2]
Out[144]: []
In [145]: alpha[-1:-5:2]
Out[145]: []
Python如何弄清楚缺少参数:
切片时,如果忽略任何参数,Python会尝试自动弄清楚它。
如果您检查CPYTHON的源代码,您将找到一个称为Pyslice_getindicesex的函数,该函数将索引删除到任何给定参数的切片。这是Python中的逻辑等效代码。
此函数需要一个用于切片的对象和可选参数,用于切片和返回的启动,停止,步骤和切片长度,用于所需的切片。
def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):
length = len(obj)
if step is None:
step = 1
if step == 0:
raise Exception("Step cannot be zero.")
if start is None:
start = 0 if step > 0 else length - 1
else:
if start < 0:
start += length
if start < 0:
start = 0 if step > 0 else -1
if start >= length:
start = length if step > 0 else length - 1
if stop is None:
stop = length if step > 0 else -1
else:
if stop < 0:
stop += length
if stop < 0:
stop = 0 if step > 0 else -1
if stop >= length:
stop = length if step > 0 else length - 1
if (step < 0 and stop >= start) or (step > 0 and start >= stop):
slice_length = 0
elif step < 0:
slice_length = (stop - start + 1)/(step) + 1
else:
slice_length = (stop - start - 1)/(step) + 1
return (start, stop, step, slice_length)
这是切片背后的智力。由于Python具有内置的功能称为slice,因此您可以传递一些参数并检查其智能计算丢失参数的智能。
In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']
In [22]: s = slice(None, None, None)
In [23]: s
Out[23]: slice(None, None, None)
In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)
In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]
In [26]: s = slice(None, None, -1)
In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]
In [28]: s = slice(None, 3, -1)
In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]
笔记: 这篇文章最初写在我的博客中 http://www.avilpage.com/2015/03/a-slice-of-python-intelligence-behind.html
这只是提供一些额外信息...请考虑以下列表
>>> l=[12,23,345,456,67,7,945,467]
逆转列表的其他技巧很少:
>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]
>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
一般而言,编写带有许多硬编码索引值的代码会导致可读性和维护混乱。例如,如果您一年后回到代码,您会查看它,想知道写它时在想什么。显示的解决方案只是一种更清楚地说明您的代码实际在做什么的方法。通常,内置slice()创建一个切片对象,可以在任何允许切片的地方使用。例如:
>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]
如果您有一个切片实例S,则可以分别查看其S.Start,S.Stop和S.Step属性来获得有关它的更多信息。例如:
>>> a = slice(10, 50, 2) >>> a.start 10 >>> a.stop 50 >>> a.step 2 >>>
1.切片符号
为了简单,请记住 切片只有一种形式:
s[start:end:step]
这就是它的工作原理:
s
: :可以切成薄片的对象start
: :第一个开始迭代的索引end
: :最后索引, 注意end
索引将不包括在结果的切片中step
: :每个选择元素step
指数
另一件事: 全部 start
,end
, step
可以省略! 如果省略它们,将使用其默认值: 0
,len(s)
,1
因此。
因此可能的变化是:
# mostly used variations
s[start:end]
s[start:]
s[:end]
# step related variations
s[:end:step]
s[start::step]
s[::step]
# make a copy
s[:]
注意:如果 start>=end
(仅考虑什么时候 step>0
),python将返回空的切片 []
.
2.陷阱
上一部分解释了切片的工作原理的核心功能,它将在大多数情况下起作用。但是,您应该提防陷阱,这部分解释了它们。
负索引
第一件事使Python学习者感到困惑 索引可能是负面的! 不要恐慌: 负索引意味着向后计数。
例如:
s[-5:] # start at the 5th index from the end of array,
# thus returns the last 5 elements
s[:-5] # start at index 0, end until the 5th index from end of array,
# thus returns s[0:len(s)-5]
负步
使事情更令人困惑 step
也可能是负面的!
负步骤意味着迭代阵列向后:从末端到启动,其中包含终端索引,并从结果中排除了启动索引。
笔记: :当步骤为负时,默认值 start
至 len(s)
(尽管 end
不等于 0
, , 因为 s[::-1]
包含 s[0]
)。例如:
s[::-1] # reversed slice
s[len(s)::-1] # same as above, reversed slice
s[0:len(s):-1] # empty list
超出范围错误?
惊奇: 当索引超出范围时,切片不会引起索引!
如果索引不在范围内,Python将尝试最佳设置索引 0
或者 len(s)
根据情况。例如:
s[:len(s)+5] # same as s[:len(s)]
s[-len(s)-5::] # same as s[0:]
s[len(s)+5::-1] # same as s[len(s)::-1], same as s[::-1]
3.示例
让我们用示例结束这个答案,说明了我们讨论过的所有内容:
# create our array for demonstration
In [1]: s = [i for i in range(10)]
In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [3]: s[2:] # from index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]
In [4]: s[:8] # from index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]
In [5]: s[4:7] # from index 4(included) up to index 7(excluded)
Out[5]: [4, 5, 6]
In [6]: s[:-2] # up to second last index(negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]
In [7]: s[-2:] # from second last index(negative index)
Out[7]: [8, 9]
In [8]: s[::-1] # from last to first in reverse order(negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
In [9]: s[::-2] # all odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]
In [11]: s[-2::-2] # all even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]
In [12]: s[3:15] # end is out of range, python will set it to len(s)
Out[12]: [3, 4, 5, 6, 7, 8, 9]
In [14]: s[5:1] # start > end, return empty list
Out[14]: []
In [15]: s[11] # access index 11(greater than len(s)) will raise IndexError
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]
IndexError: list index out of range
上面的答案没有讨论使用著名的numpy软件包的多维阵列切片:
切片也可以应用于多维阵列。
# Here, a is a NumPy array
>>> a
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
[5, 7]])
这 ”:2
“逗号在第一个维度上运行之前”0:3:2
“逗号在第二维度上运行之后。
#!/usr/bin/env python
def slicegraphical(s, lista):
if len(s) > 9:
print """Enter a string of maximum 9 characters,
so the printig would looki nice"""
return 0;
# print " ",
print ' '+'+---' * len(s) +'+'
print ' ',
for letter in s:
print '| {}'.format(letter),
print '|'
print " ",; print '+---' * len(s) +'+'
print " ",
for letter in range(len(s) +1):
print '{} '.format(letter),
print ""
for letter in range(-1*(len(s)), 0):
print ' {}'.format(letter),
print ''
print ''
for triada in lista:
if len(triada) == 3:
if triada[0]==None and triada[1] == None and triada[2] == None:
# 000
print s+'[ : : ]' +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] == None and triada[2] != None:
# 001
print s+'[ : :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] != None and triada[2] == None:
# 010
print s+'[ :{0:2d} : ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] == None and triada[1] != None and triada[2] != None:
# 011
print s+'[ :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] == None and triada[2] == None:
# 100
print s+'[{0:2d} : : ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] == None and triada[2] != None:
# 101
print s+'[{0:2d} : :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] != None and triada[2] == None:
# 110
print s+'[{0:2d} :{1:2d} : ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif triada[0] != None and triada[1] != None and triada[2] != None:
# 111
print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
elif len(triada) == 2:
if triada[0] == None and triada[1] == None:
# 00
print s+'[ : ] ' + ' = ', s[triada[0]:triada[1]]
elif triada[0] == None and triada[1] != None:
# 01
print s+'[ :{0:2d} ] '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
elif triada[0] != None and triada[1] == None:
# 10
print s+'[{0:2d} : ] '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
elif triada[0] != None and triada[1] != None:
# 11
print s+'[{0:2d} :{1:2d} ] '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]
elif len(triada) == 1:
print s+'[{0:2d} ] '.format(triada[0]) + ' = ', s[triada[0]]
if __name__ == '__main__':
# Change "s" to what ever string you like, make it 9 characters for
# better representation.
s = 'COMPUTERS'
# add to this list different lists to experement with indexes
# to represent ex. s[::], use s[None, None,None], otherwise you get an error
# for s[2:] use s[2:None]
lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]
slicegraphical(s, lista)
您可以运行此脚本并对其进行实验,下面是我从脚本中获得的一些示例。
+---+---+---+---+---+---+---+---+---+
| C | O | M | P | U | T | E | R | S |
+---+---+---+---+---+---+---+---+---+
0 1 2 3 4 5 6 7 8 9
-9 -8 -7 -6 -5 -4 -3 -2 -1
COMPUTERS[ 4 : 7 ] = UTE
COMPUTERS[ 2 : 5 : 2 ] = MU
COMPUTERS[-5 : 1 :-1 ] = UPM
COMPUTERS[ 4 ] = U
COMPUTERS[-4 :-6 :-1 ] = TU
COMPUTERS[ 2 :-3 : 1 ] = MPUT
COMPUTERS[ 2 :-3 :-1 ] =
COMPUTERS[ : :-1 ] = SRETUPMOC
COMPUTERS[-5 : ] = UTERS
COMPUTERS[-5 : 0 :-1 ] = UPMO
COMPUTERS[-5 : :-1 ] = UPMOC
COMPUTERS[-1 : 1 :-2 ] = SEUM
[Finished in 0.9s]
使用负面步骤时,请注意答案被转移到右侧1。
我的大脑似乎很高兴接受 lst[start:end]
包含 start
- 项目。我什至可能会说这是一个“自然假设”。
但是偶尔有疑问会蔓延,我的大脑要求放心,它不包含 end
- 元素。
在这些时刻,我依靠这个简单的定理:
for any n, lst = lst[:n] + lst[n:]
这个漂亮的财产告诉我 lst[start:end]
不包含 end
- 因为它在 lst[end:]
.
请注意,此定理对任何人都是正确的 n
根本。例如,您可以检查
lst = range(10)
lst[:-42] + lst[-42:] == lst
返回 True
.
在Python中,切片最基本的形式是:
l[start:end]
在哪里 l
是一些集合, start
是包容性指数, end
是一个独家索引。
In [1]: l = list(range(10))
In [2]: l[:5] # first five elements
Out[2]: [0, 1, 2, 3, 4]
In [3]: l[-5:] # last five elements
Out[3]: [5, 6, 7, 8, 9]
从一开始就切片时,您可以省略零索引,而将其切成终止时,您可以省略最终索引,因为它是多余的,因此请勿冗长:
In [5]: l[:3] == l[0:3]
Out[5]: True
In [6]: l[7:] == l[7:len(l)]
Out[6]: True
相对于集合的结尾进行偏移时,负整数很有用:
In [7]: l[:-1] # include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
In [8]: l[-3:] # take the last 3 elements
Out[8]: [7, 8, 9]
切片时,可以提供超出界限的索引,例如:
In [9]: l[:20] # 20 is out of index bounds, l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [11]: l[-20:] # -20 is out of index bounds, l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
请记住,切片收藏的结果是一个全新的收藏。另外,在分配中使用切片符号时,切片分配的长度不必相同。保留分配的切片之前和之后的值,收集将收缩或成长以包含新值:
In [16]: l[2:6] = list('abc') # assigning less elements than the ones contained in the sliced collection l[2:6]
In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]
In [18]: l[2:5] = list('hello') # assigning more elements than the ones contained in the sliced collection l [2:5]
In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]
如果您省略了开始索引和结束索引,则将制作该集合的副本:
In [14]: l_copy = l[:]
In [15]: l == l_copy and l is not l_copy
Out[15]: True
如果执行作业操作时省略了开始和端索引,则该集合的整个内容将被替换为所引用的内容的副本:
In [20]: l[:] = list('hello...')
In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']
除了基本切片外,还可以应用以下符号:
l[start:end:step]
在哪里 l
是一个集合, start
是包容性指数, end
是一个独家指数, step
是可以用来采取每一个的大步 nth 项目IN l
.
In [22]: l = list(range(10))
In [23]: l[::2] # take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]
In [24]: l[1::2] # take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]
使用 step
提供了一个有用的技巧来扭转Python中的收藏:
In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
也可以将负整数用于 step
作为以下示例:
In[28]: l[::-2]
Out[28]: [9, 7, 5, 3, 1]
但是,使用负值 step
可能会变得非常混乱。此外,为了成为Pythonic,您应该避免使用 start
, end
, , 和 step
在一个切片中。如果需要,请考虑在两个任务中进行此操作(一个要切片,另一个大步向前)。
In [29]: l = l[::2] # this step is for striding
In [30]: l
Out[30]: [0, 2, 4, 6, 8]
In [31]: l = l[1:-1] # this step is for slicing
In [32]: l
Out[32]: [2, 4, 6]
以下是字符串索引的示例
+---+---+---+---+---+
| H | e | l | p | A |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1
str="Name string"
切片示例:[start:end:step
str[start:end] # items start through end-1
str[start:] # items start through the rest of the array
str[:end] # items from the beginning through end-1
str[:] # a copy of the whole array
以下是示例用法
print str[0]=N
print str[0:2]=Na
print str[0:7]=Name st
print str[0:7:2]=Nm t
print str[0:-1:2]=Nm ti
我想添加一个Hello World示例,该示例解释了初学者的切片基础知识。这对我帮助很大。
让我们有一个六个值的列表 ['P', 'Y', 'T', 'H', 'O', 'N']
:
+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
0 1 2 3 4 5
现在,该列表中最简单的切片是其冠军。符号是 [<index>:<index>]
关键是这样阅读:
[ start cutting before this index : end cutting before this index ]
现在,如果您做了切片 [2:5]
在上面的列表中,这将发生:
| |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
0 1 | 2 3 4 | 5
你削减了 前 带有索引的元素 2
还有另一个削减 前 带有索引的元素 5
. 。因此,结果将是这两个切口之间的切片 ['T', 'H', 'O']
.
我认为,如果您以下方式查看(请继续阅读),您会理解并记住Python字符串切片符号。
让我们使用以下字符串...
azString = "abcdefghijklmnopqrstuvwxyz"
对于那些不知道的人,您可以从 azString
使用符号 azString[x:y]
来自其他编程语言,这是常识被妥协的时候。什么是X和Y?
我不得不坐下来运行几种场景,以寻求一种记忆技术,这将帮助我记住X和Y是什么,并在第一次尝试时可以正确切片串。
我的结论是,X和Y应该被视为我们想要额外的弦的边界索引。因此,我们应该将表达视为 azString[index1, index2]
甚至更清晰 azString[index_of_first_character, index_after_the_last_character]
.
这是一个示例可视化...
Letters a b c d e f g h i j ...
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
Indexes 0 1 2 3 4 5 6 7 8 9 ...
| |
cdefgh index1 index2
因此,如果要将index1和index2设置为所需的子字符串的值,则需要做的一切。例如,要获取子字符串“ cdefgh”,您可以使用 azString[2:8]
因为“ C”左侧的索引为2,而“ H”的右侧索引为8。
请记住,我们正在设定边界。这些边界是您可以在这样的位置放置一些括号的位置。
ab [ CDEFGH ] IJ
这个技巧一直在工作,很容易记住。
如果您在切片中感到负面索引令人困惑,这是考虑一下的非常简单的方法:只需将负面索引替换为 len - index
. 。因此,例如,将-3替换为 len(list) - 3
.
说明切片在内部执行的最佳方法是在实现此操作的代码中显示它:
def slice(list, start = None, end = None, step = 1):
# take care of missing start/end parameters
start = 0 if start is None else start
end = len(list) if end is None else end
# take care of negative start/end parameters
start = len(list) + start if start < 0 else start
end = len(list) + end if end < 0 else end
# now just execute for-loop with start, end and step
return [list[i] for i in range(start, end, step)]
基本的切片技术是定义起点,停止点和步长 - 也称为步幅。
首先,我们将创建一个值列表,以在我们的切片中使用。
创建两个列表要切片,第一个是1到9的数字列表(列表a)。第二个也是数字列表,从0到9(列表B)
A = list(range(1,10,1)) # start,stop,step
B = list(range(9))
print("This is List A:",A)
print("This is List B:",B)
索引A的数字3和B的数字6。
print(A[2])
print(B[6])
基本切片
用于切片的扩展索引语法为alist [start:stop:step]。启动参数和步骤参数都默认为无 - 唯一必需的参数是停止。您是否注意到这类似于如何使用范围定义列表A和B?这是因为切片对象表示按范围指定的索引集(开始,停止,步骤)。 Python 3.4文档
如您所见,仅定义停止将返回一个元素。由于启动默认为none,因此转化为仅检索一个元素。
重要的是要注意,第一个元素是索引0,而不是索引1。这就是为什么我们使用2个列表进行此练习的原因。列表A的元素按顺序位置编号(第一个元素是1,第二个元素为2等),而List B的元素是用于索引它们的数字(第一个元素0,[0]等) )。
使用扩展索引语法,我们检索了一系列值。例如,用结肠检索所有值。
A[:]
要检索元素的子集,需要定义开始和停止位置。
给定图案alist [start:stop],从列表中检索前两个元素
如果我们可以将切片与 range
, ,这给出了索引。我们可以将切片分类为以下两个类别:
1.没有步骤> 0。例如 [i:j]
或者 [i:j:k]
(k> 0)
假设序列是 s=[1,2,3,4,5]
.
- 如果
0<i<len(s)
和0<j<len(s)
, , 然后[i:j:k] -> range(i,j,k)
例如, [0:3:2] -> range(0,3,2) -> 0, 2
- 如果
i>len(s)
或者j>len(s)
, , 然后i=len(s)
或者j=len(s)
例如, [0:100:2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4
- 如果
i<0
或者j<0
, , 然后i=max(0,len(s)+i)
或者j=max(0,len(s)+j)
例如, [0:-3:2] -> range(0,len(s)-3,2) -> range(0,2,2) -> 0
另一个例子, [0:-1:2] -> range(0,len(s)-1,2) -> range(0,4,2) -> 0, 2
- 如果
i
未指定,然后i=0
例如, [:4:2] -> range(0,4,2) -> range(0,4,2) -> 0, 2
- 如果
j
未指定,然后j=len(s)
例如, [0::2] -> range(0,len(s),2) -> range(0,5,2) -> 0, 2, 4
2.步骤<0。 [i:j:k]
(k <0)
假设序列是 s=[1,2,3,4,5]
.
- 如果
0<i<len(s)
和0<j<len(s)
, , 然后[i:j:k] -> range(i,j,k)
例如, [5:0:-2] -> range(5,0,-2) -> 5, 3, 1
- 如果
i>len(s)
或者j>len(s)
, , 然后i=len(s)-1
或者j=len(s)-1
例如, [100:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2
- 如果
i<0
或者j<0
, , 然后i=max(-1,len(s)+i)
或者j=max(-1,len(s)+j)
例如, [-2:-10:-2] -> range(len(s)-2,-1,-2) -> range(3,-1,-2) -> 3, 1
- 如果
i
未指定,然后i=len(s)-1
例如, [:0:-2] -> range(len(s)-1,0,-2) -> range(4,0,-2) -> 4, 2
- 如果
j
未指定,然后j=-1
例如, [2::-2] -> range(2,-1,-2) -> 2, 0
另一个例子, [::-1] -> range(len(s)-1,-1,-1) -> range(4,-1,-1) -> 4, 3, 2, 1, 0
总之
我不认为 Python教程 图(在其他各种答案中引用)很好,因为该建议适用于正向大步前进,但对负面步不利。
这是图:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
从图中,我希望 a[-4,-6,-1]
成为 yP
但它是 ty
.
>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'
总是有效的是在角色或插槽中思考,并将索引用作半开的间隔 - 如果积极的速度,则是右开的,如果是负面的,则左开了。
这样,我可以想到 a[-4:-6:-1]
作为 a(-6,-4]
在间隔术语中。
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
+---+---+---+---+---+---+---+---+---+---+---+---+
| P | y | t | h | o | n | P | y | t | h | o | n |
+---+---+---+---+---+---+---+---+---+---+---+---+
-6 -5 -4 -3 -2 -1 0 1 2 3 4 5