列表元素支持用索引访问,正向索引从0开始
colors=["red","blue","green"]
colors[0] =="red"
colors[1]=="blue"
同时,也可以使用负向索引(python中有序序列都支持负向索引)
colors[-1]=="green"
以列表 lst = [ 'a', 'b', 'c', 'd', 'e' ]
为例
[ start_index : end_index : step ]
切片适用于列表、元组、字符串、range对象等类型。由于集合、字典等无序,无法实现切片。
切片的返回结果类型和切片对象类型一致,返回的是切片对象的子序列,如:对一个列表切片返回一个列表,字符串切片返回字符串。
切片生成的子序列元素是源版的拷贝。因此切片是一种浅拷贝。
**【注意:】**切片的开始总是被包括在结果中,而结束不被包括。
即:
[0:len(lst):1] —— 开始索引为 0,结束索引为 len(lst) ,而切到的最后一个元素的索引为 len(lst)-1 ;
[-1:-len(lst):-1] —— 开始索引为-1,结束索引为 -len(lst) ,而切到的最后一个元素的索引为 -(len(lst)-1) 。
因此,切片时对象的索引可以看做下图形式:
总是从 start_index 开始切取元素。
正索引与负索引可以混用,且 start_index 和 end_index 以及 step 不受当前列表对象的实际索引长度限制,可以认为取值是(-∞,+∞)。(切片中的越界索引会被自动处理。详见后文)
start到end的区间方向和step方向相反,则返回空列表 []
。
#例如:
>>>lst[1:4:-1]
[] #返回空列表
start == end 时,返回空列表[]
。
#例如:
>>>lst[1:1:]
[] #返回空列表
>>>lst[-4:-4:]
[] #返回空列表
【注意:】正式句法规则并没有在序列中设置负标号的特殊保留条款。
但是,切片序列是通过__getitem__() 方法解析索引的,内置序列所提供的 getitem() 方法可通过在索引中添加序列长度来解析负标号 (这样 x[-1] 会选出 x 中的最后一项)。 结果值必须为一个小于序列中项数的非负整数,抽取操作会选出标号为该值的项(从零开始数)。 由于对负标号和切片的支持存在于对象的 getitem() 方法,重载此方法的子类需要显式地添加这种支持。
object.__getitem__(self, key)
调用此方法以实现 self[key] 的求值。对于序列类型,接受的键应为整数和切片对象。
请注意负数索引(如果类想要模拟序列类型)的特殊解读是取决于 __getitem__() 方法。
如果 key 的类型不正确则会引发 TypeError 异常;
如果为序列索引集范围以外的值(在进行任何负数索引的特殊解读之后)则应引发 IndexError 异常。
对于映射类型,如果 key 找不到(不在容器中)则应引发 KeyError 异常。
注解:for 循环在有不合法索引时会期待捕获 IndexError 以便正确地检测到序列的结束。
注意:
>>>lst[-5:] #此时,endindex默认为5,step默认为1
[a,b,c,d,e]
>>>lst[-5:-1]
[a,b,c,d]
>>>lst[-5:5] #等价于lst[-5:]
[a,b,c,d,e]
切片中的越界索引会被自动处理。
切片用于创建新列表。如果索引区间不在列表中元素数量的范围内,我们可以返回一个空列表。所以,我们不必抛出错误。
但是,如果我们直接访问列表中大于元素数量的元素,我们就不能返回任何默认值(甚至不是None,因为它可能是列表中的有效值)。这就是为什么IndexError: list index out of range
在切片中被扔了。
可选参数 start 和 end 是切片符号,用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是 start 参数。(切片索引会被静默截短到允许的范围;如果指定索引不是整数则 TypeError 会被引发。)
切片时,如果切片区间不在该对象的实际索引范围类,则将返回序列的长度设置为0,this line中
defstop = *step < 0 ? -1 : length;
...
if (r->stop == Py_None) {
*stop = defstop;
}
...
if ((*step < 0 && *stop >= *start)
|| (*step > 0 && *start >= *stop)) {
*slicelength = 0;
对于字符串,如果切片后返回的字符串长度为0,则返回空字符串,this line中
if (slicelength <= 0) {
return PyString_FromStringAndSize("", 0);
}
其他可切片对象参照以上代码,这里不再引述。
切片生成一个新的序列,并且把原序列中所有元素的引用都复制到新序列中。这意味着以下切片操作会返回列表的一个 潜复制 。
Python 中赋值语句不复制对象,而是在目标和对象之间创建绑定 (bindings) 关系。对于自身可变或者包含可变项的集合对象,开发者有时会需要生成其 副本 用于改变操作,进而避免改变原对象。
浅复制构造一个新的复合对象,然后(尽可能地)插入原始对象包含的相同对象。
( 相当于原aList[0]
与切片的bList[0]
作为 同一个元素值的引用 共同指向第一个元素1
的地址空间。)
(1)潜复制不会引发错误的情况:浅复制列表中只包含可哈希对象时(即地指定位值唯一的对象。如值类型、字符串、元组等。)
程序如下,虽然aList[0]
和aList[0]
共同指向第一个元素1
的地址空间,但是由于1
是值类型是不可变对象,因此改变aList[0]
的元素值时,不能通过 aList[0]
指针改变该地址空间的 1
,只能通过指向其他地址空间来实现元素值的改变。而bList
为潜复制,bList[0]
是 1
的引用(指针),bList[0]
任然指向 1
的地址空间。此时潜复制没有表现出危害性。
>>>aList[1,2,3]
>>>bList = alist[:]
>>>aList[1] = 0
>>>aList
[1,0,3]
>>>bList
[1,2,3] #修改aList不影响bList
>>>aList[1,2,3]
>>>bList = aList[:]
>>>bList[0] = 0
>>>aList
[1,2,3] #修改bList也不影响aList
>>>bList
[0,2,3]
(2)潜复制会引发错误的情况:浅复制列表中包含不可哈希对象时(即可改变的对象,引用对象。如列表、字典等。)
程序如下,此时,可变对象[2]
为引用类型,
[2]
原地操作时,aList[1]
和bList[1]
始终指向对象[2]
,因此原地操作修改[2]
为[2,5]
时,aList[1]
和bList[1]
都影响。aList[1] = [2,5]
对[2]
非原地操作时,根据python的内存管理[2]
和[2,5]
地址空间不同,为两个不同对象,aList[1]
指向新元素[2,5]
,bList[1]
保持原状指向[2]
。无名可变对象:
>>>aList[1,[2],3] #元素[2]为无名可变对象,临时可变对象
>>>bList = aList[:]
>>>aList[1].append(5) #原地操作。注意:若不原地操作,修改前后不是同一对象。
>>>aList
[1, [2, 5], 3]
>>>bList
[1, [2, 5], 3] #修改aList的可变对象元素时,会影响bList。反之亦然。(修改不可变对象时依旧互不影响)
有名可变对象:
>>>lst = [2]
>>>aList[1,lst,3] #元素lst为可变对象
>>>bList = aList[:]
>>>lst.append(5) #原地操作。注意:若不原地操作,修改前后不是同一对象。
>>>aList
[1, [2, 5], 3]
>>>bList
[1, [2, 5], 3] #修改可变对象元素时,会同时影响aList和bList。(修改不可变对象时依旧互不影响)
可变对象非原地操作时:
>>>aList[1,[2],3] #元素[2]为无名可变对象,临时可变对象
>>>bList = aList[:]
>>>aList[1] = [2,5] #非原地操作,修改前后不是同一对象。即[2]和[2,5]地址空间不同。
>>>aList
[1, [2, 5], 3]
>>>bList
[1, [2], 3] #非原地操作,改变aList的可变对象元素,不会影响bList。反之亦然。(此时与修改非可变对象同理)
递归复制,存储空间独立。(消除了潜复制带来的隐患)
深复制构造一个新的复合对象,然后递归地将在原始对象中找到的对象插入到复合对象中。( 相当于找到最终的值类型,再独立开辟空间存储一份。)
>>>import copy
>>>aList = [1,[2],3]
>>>bList = copy.deepcopy(aList) #深复制,aList和bList完全独立,互不影响
>>>aList[1].append(5) #对[2]原地操作
>>>aList
[1,[2,5],3] #修改bList不影响aList。反之亦然。(修改其他元素——可变或非可变对象 也是如此)
>>>bList
[1,[2],3]
在当前操作过程中保存已经复制的对象表。
让用户定义的类重写复制操作或复制的组件集。
【 不复制模块、类、函数、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组等类型,也不复制任何类似的类型。类可以使用与控制pickle相同的接口来控制复制:它们可以定义称为余下的getinitargs__()、余下的getstate__()和余下的setstate__()的方法。“pickle”可以获得关于这些方法的信息。】
以列表 lst = [ 'a', 'b', 'c', 'd', 'e' ]
为例
1.切取整个列表
>>>lst[::1]
['a', 'b', 'c', 'd', 'e']
2.切取倒叙的列表
>>>lst[::-1]
['e', 'd', 'c', 'b', 'a']
3.省略step以及第二个冒号
>>>lst[:] #step默认为1
['a', 'b', 'c', 'd', 'e']
4.正负索引混用
>>>lst[0:-2:1]
['a', 'b', 'c']
5.切片索引越界
>>>[-5:10:]
['a', 'b', 'c', 'd', 'e']
6.列表内的空区间
>>>lst[0:-5:]
[]
7.列表外的区间
>>>lst[5:10:]
[]
>>>lst[-5:-1:]
[]
8.start到end方向与step方向相反
>>>lst[1:3:-1]
[]
9.连续切片
>>>lst[0:4][0:3][1:4] #可无限次连续切片操作
['b', 'c']
'''
等价于
>>> x = lst[0:4]
>>> y = x[0:3]
>>> z = y[1:4]
>>> z
['b', 'c']
'''
10.参数可用多项表达式
>>>lst[2+1:3*2:7%3] # 即:a[2+1:3*2:7%3] = a[3:6:1]
['d', 'e']
11.修改多个元素
>>>lst[1:3] = ['A','B','C']
['a', 'A', 'B', 'C', 'd', 'e']
【注意:】
>>>lst[-1:] #此时step默认=1,因此end_index采用正索引默认值=len(lst)
['e']