理解python索引和切片

许多初次接触Python的人对于索引都会有同样的反应:这太奇怪了。在Python的列表,字符串和条件语句中都充斥着索引,但在我们习惯他们之前, 这些都会是我们程序的错误来源。因此,让我们硬着头皮上吧!

这片文章会写得很慢,为了解释一些基础和默认的东西。

我们会使用字符串切片举例,因为这事我们首先接触的,不过这对于列表切片和设定范围是一样的。我们有:

a = '0123456789'

其中第k个位置的字符为k。

我们使用如下方式对a进行切片:

b = a[start:stop:step]

或者直接地:

'0123456789'[start:stop:step]

在Python中,字符串和指向字符串的变量都是对象,所以都可以进行切片(事实上,Python中所有东西都是对象:数字,变量,字符串,函数,文件)。

有三件事情需要记住:

  1. start 是我们想要的第一项(当然)
  2. stop 是我们第一个不想要的项
  3. step 可以是正整数,也可以是负数,定义了向前(从第一个位置到向末尾)还是向后(从最后一个位置向开始位置)索引。

一个小提醒:但我们学习python之外其他语言的时候,stop的定义是Python索引和切片在熟悉其语言的程序员看来如此奇怪的原因之一。在大部分计算机 语言中,stop应该是“我们需要的最后一项”。不管这个定义是否比其他语言更好或者更坏,Python的确是不寻常的一种语言。

在索引中使用负数是python另一个奇怪的特性。在大部分C衍生的语言(C/C++/C#,Java,,Javascript等)中,负数索引是不合法的,因为索引表示从 字符串初始内存地址的偏移,所以负数索引会指向字符串开始位置之前的位置(更详细的请参见这篇博客: 为什么python中索引从0开始)。然而,Python并不是唯一使用负数索引的语言,比如, Perl像python一样使用负数索引来表示从字符串末尾开始的位置; R语言面向统计,A[-i]表示所有除第i位置的值。不过,只有很少的语言 在任意情况下使用负数索引。

现在让我们回到Python索引上。

** 使用正数和负数索引 **

我们可以使用正整数表示字符串中的位置,由字符串开头从0开始计数:

b = "my mistress' eyes are nothing like the sun"
     ^         ^                              ^
    b[0]      b[10]                          b[41]

我们用len()函数来获取一个字符串的长度。因此,b有42个字符,故len(b)=42。因为b最后一个字符是b[41],所以len(b)比b字符串最后位置索引多1。

有些时候这对从字符串末尾开始找字符是很有帮助的。所以我们用负数,从字符串末尾开始计数,即从-1(不是0):

b = "my mistress' eyes are nothing like the sun"
     ^         ^                              ^
   b[-42]   b[-32]                          b[-1]

从末尾看的最后一个字符串是b[-42],那么它前面的位置会是-len(b)-1 = -43。

所以,在这个例子中len(b) = 42

b[0]        = b[-len(b)] = b[-42] = 'm'
b[len(b)-1] = b[-1]      = b[41]  = 'n'
b[10]       = b[-32]              = 's'

并且一般地,

b[k]        = b[-len(b)+k]

hmm, 这很令人疑惑。我们需要了解索引的一般机制,但是,我们不需要记住这些边界值,这些是python中默认的。

默认值是引用一个变量的时候,我们没有指定明确的值。这就和我们“默认地”称呼一位女性为女士(Ms.)一样。如果我们没有被告知需要称呼她为太太(Mrs.)或者小姐(Miss),或者如果我们忽略了实际的尊称,我们降退回到“默认”值。Python对于很常用的start, stop and step都有默认值。

** 如果step是正数,我们向前移动(如果step值为空,则默认为+1)**

a[2:6] = '0123456789'[2:6:1] = '2345'

如上例,我们想要的第一个值在第二个位置,第一个不想要的在第6个位置。

更进一步,我们从字符串末尾开始看:

a[-8:-4] = '0123456789'[-8:-4:1] = '2345'

我们想要的第一个值是从后数第8个(2),第一个不想要的是从后数第4个(6)。

所以,对于任意正数step,我们有如下的默认值:

     |-> -> ->|
a = '0123456789'
     ^         ^
  start:0   stop: len(a), i.e, 超出了字符串的末尾的位置

所以:

a[:]    = a[0:len(a):1] = '0123456789' # a +1 step 默认
a[::2]  = a[0:len(a):2] = '02468'      # 所有偶数位置
a[1::2] = '13579'                      # 所有奇数位置
a[::3]  = '0369'                       # 所有3的倍数位置

所以,只要我们从字符串头部或者尾部使用切片,Python都会使用默认值。

** 如果step是负数,而我们从后往前数**

a[6:2:-1] = '0123456789'[6:2:-1] = '6543'

我们需要的第一个值在第六个位置,不需要的第一个值在第二个位置。

或者进一步,

a[-4:-8:-1] = '0123456789'[-4:-8:-1] = '6543'

我们想要的第一个值是从后数第4个(6),第一个不想要的是从后数第8个(2)。

注意到我们可以在索引中使用正数或者负数,以及从前或者从后搜寻字符串,所以我们甚至可以将它们混合起来使用:

a[6:-8:-1] = '6543'
a[-4:2:-1] = '6543'

有时候这样的混合式非常方便的:

url = '<a href="http://udacity.com">'[9:-2]
    = 'http://udacity.com'

我们需要记住的是使用负数索引不意味着我们就是向后移动,只是我们从字符串末尾开始索引。向前还是向后是仅由step变量的符号决定的。

为了向后移动,我们需要在我们的意识中反转这个字符串:

     |<- <- <-|
a = '0123456789'
    ^         ^
    ^      start:-1
stop:在字符串开始位置之前的位置

所以:

a[::-1] = a[-1::-1] = '9876543210' # 我们只是学习如何反转字符串
a[::-2] = a[-1:-len(a)-1:-2] = '97531'
a[::-3] = a[-1:-len(a)-1:-3] = '9630'

再一次,只要我们从字符串的头部或者尾部对字符串切片,我们可以使用空的start和stop变量,Python会使用默认值。

只用6个字符就反转了一个字符串,厉害吧!可惜的是这个只在Python中有用,许多其他的语言并不支持这种方式。这类问题只是用来是我们熟悉这种结构,不只是在Python中,也包括其他语言在内。因此,考虑需要反转字符串的这类问题(比如回文问题)可以让我们学习如何使用循环,索引,并且尝试不同切片来解决这些问题。这样,你就有两手准备了。


所以现在,我们已经掌握了Python的索引,应该能明白底下这些了:

'0123456789'[8:2:-2]   = '864'
'0123456789'[8:-8:-2]  = '864'
'0123456789'[-2:2:-2]  = '864'
'0123456789'[-2:-8:-2] = '864'

Good luck!

原文在这儿

View on GitHub