前言
在讲Python中的列表之前,首先要引入字符串。
在Python中,字符串与列表都属于有序容器,有序意味着可以通过索引以及切片的方式对容器中的元素进行定位。另外,字符串与列表的区别在于字符串是不可变容器,意味着不能修改字符串的内容。
字符串的索引与切片
字符串索引
在Python中,字符串被视为一种不可变序列,序列意味着可以使用索引以及切片来定位字符串中的元素,而不可变则意味着所有对字符串的修改操作都是非法的。
索引是一种定位字符串中单个字符的做法,在Python中,索引分为正序索引和逆序索引。而对于字符串这个具体的对象,索引是指字符串中的每个字符在字符串中的位置。
正序索引从字符串起始处,从左往右分别给字符串中的字符编号,从0开始依次递增;逆序索引从字符串结尾处,从右往左分别给字符串中的字符编号,从-1开始依次递减。使用(字符串[索引下标])的方法,从字符串中定位单个字符。
举例:对于"一起学习Python自动化测试"这个字符串,我要定位它的第一个字符和最后一个字符,则可以采用如下的形式。
str01 = "一起学习python自动化测试"
print(str01[0])
print(str01[-1])
上面程序的终端输出如下:
一
试
注意:索引值不能超过字符串长度,否则会报IndexError。
In [7]: print(str01[15])
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-7-92262652de33> in <module>
----> 1 print(str01[15])
IndexError: string index out of range
测试自学教程这里获取… https://csx-t231.github.io/
字符串切片
利用索引可以定位字符串中单个字符,当需要以某种形式获取字符串的子串时,切片就登场了。
切片的语法是:容器[开始索引:结束索引:步长],表示从容器的开始索引处开始,到结束索引处为止,每隔一定的步长取出容器中的元素,并组合成一个新的容器,新容器的类型与原容器相同。
这里的容器可以是Python中任意类型的有序容器,包括字符串、列表、元组等等。
开始索引默认为0,步长默认为1,如果不指定结束索引,则默认索引到容器的最后一个元素。步长为负数,表示从字符串末尾往前索引(逆序)。
还是以上面索引的字符串为例,使用切片获取字符串的子串,用法如下:
str01 = "一起学习python自动化测试"
# 获取字符串的前4个字符
str02 = str01[:4:]
print(str02)
# 获取字符串的第3到第10个字符
str02 = str01[2:10:]
print(str02)
# 获取字符串的末尾5个字符
str02 = str01[-5::]
print(str02)
# 获取字符串的前4个字符,步长为2
str02 = str01[:4:2]
print(str02)
# 获取字符串的第3到第10个字符,步长为3
str02 = str01[2:10:3]
print(str02)
# 逆序输出字符串的前4个字符
str02 = str01[-12::-1]
print(str02)
# 逆序输出字符串的末尾5个字符
str02 = str01[:-6:-1]
print(str02)
上面程序的终端输出分别如下:
一起学习
学习python
自动化测试
一学
学yo
习学起一
试测化动自
列表切片
与字符串一样,列表也是Python中的一种有序容器,并且容器的内容是可变的,可以使用索引及切片按照指定的规则从容器中获取或修改元素。
假设有这样一个漫威电影宇宙,它有钢铁侠、美国队长、雷神、冬兵、蚁人、浩克、猩红女巫、女武神、星爵、格鲁特等英雄。我们给这个电影宇宙做一个列表,如下:
heroList01 = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
从容器中获取元素
与字符串操作类似,可以通过切片的方法获取列表中符合切片规则的元素,甚至获取整个列表的元素。列表的切片语法是:容器[开始索引:结束索引:步长]。
获取列表中部分元素
测试自学教程这里获取… https://csx-t231.github.io/
与字符串操作类似,我们可以获取列表中的前三个英雄、后五个英雄、第三到第七个英雄、逆序输出前三个/后五个英雄等等。
# 获取列表中的前三个英雄
heroList02 = heroList01[:3:]
print(heroList02)
# 获取列表中的后五个英雄
heroList02 = heroList01[-5::]
print(heroList02)
# 获取列表的第三到第七个英雄
heroList02 = heroList01[2:7:]
print(heroList02)
# 逆序输出列表前三个英雄
heroList02 = heroList01[-8::-1]
print(heroList02)
# 逆序输出列表后五个英雄
heroList02 = heroList01[:-6:-1]
print(heroList02)
上面程序的输出分别是:
['钢铁侠', '美国队长', '雷神']
['星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['雷神', '冬兵', '浩克', '星爵', '格鲁特']
['雷神', '美国队长', '钢铁侠']
['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵']
复制整个列表
同样,我们可以使用切片复制整个列表,并且可以是以顺序或者逆序的方式复制。
# 通过切片的形式复制列表
heroList02 = heroList01[::]
print(heroList02)
# 通过切片的形式逆序复制列表
heroList02 = heroList01[::-1]
print(heroList02)
上面程序的输出如下:
['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵', '浩克', '冬兵', '雷神', '美国队长', '钢铁侠']
使用切片时,Python以浅拷贝的方式生成列表,并赋值给新对象。这与列表赋值不同,列表赋值仅仅是简单地将列表的首地址赋值给另外一个变量。我们用内存图的方式来呈现一下列表切片复制的整个过程:
修改容器中的元素
由于列表是可变容器,我们可以修改列表中的元素,常规的修改方法是通过索引定位到列表中的某个元素然后加以修改。
除此之外,我们可以使用切片的方法对列表中的一片特定区域做修改。
还是以上面的漫威电影宇宙列表为例,我们使用三个新的英雄替换原先的英雄列表,新的英雄分别是幻视、鹰眼、蜘蛛侠。有几种替换方案:
三换三,替换后,列表的英雄数量还是10个。
originList = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
heroList01 = originList[:]
# 使用切片的方式替换heroList01的前三个元素
heroList01[:3] = heroList02
print(heroList01)
heroList01 = originList[:]
# 使用切片的方式替换heroList01的第5-7个元素
heroList01[4:7] = heroList02
print(heroList01)
heroList01 = originList[:]
# 使用切片的方式替换heroList01的末尾三个元素
heroList01[-3:] = heroList02
print(heroList01)
heroList01 = originList[:]
# 使用切片的方式替换heroList01的末尾三个元素,并且是逆序
heroList01[:-4:-1] = heroList02
print(heroList01)
heroList01 = originList[:]
# 使用切片的方式替换heroList01的前三个元素,并且是逆序
heroList01[-8::-1] = heroList02
print(heroList01)
上面程序的输出如下:
['幻视', '鹰眼', '蜘蛛侠', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['钢铁侠', '美国队长', '雷神', '冬兵', '幻视', '鹰眼', '蜘蛛侠', '蚁人', '猩红女巫', '女武神']
['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '幻视', '鹰眼', '蜘蛛侠']
['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蜘蛛侠', '鹰眼', '幻视']
['蜘蛛侠', '鹰眼', '幻视', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
由上面程序输出可以看出,使用切片的方式可以将列表1的部分元素批量替换成列表2的元素。我们以替换前三个元素为例,研究一下切片过程中的内存布局变化:
heroList01 = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
print("heroList01 的内容是:{}".format(heroList01))
print("heroList01 的地址是:{}".format(hex(id(heroList01))))
print("originList 第一个元素的地址是:{}".format(hex(id(originList[0]))))
print("heroList01 第一个元素的地址是:{}".format(hex(id(heroList01[0]))))
print("heroList02 第一个元素的地址是:{}".format(hex(id(heroList02[0]))))
heroList01[:3] = heroList02
print("heroList01 的内容是:{}".format(heroList01))
print("heroList01 的地址是:{}".format(hex(id(heroList01))))
print("originList 第一个元素的地址是:{}".format(hex(id(originList[0]))))
print("heroList01 第一个元素的地址是:{}".format(hex(id(heroList01[0]))))
print("heroList02 第一个元素的地址是:{}".format(hex(id(heroList02[0]))))
上面程序的输出如下:
heroList01 的内容是:['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
heroList01 的地址是:0x1fab98ec908
originList 第一个元素的地址是:0x1fab86f84b0
heroList01 第一个元素的地址是:0x1fab86f84b0
heroList02 第一个元素的地址是:0x1fab98f7e10
heroList01 的内容是:['幻视', '鹰眼', '蜘蛛侠', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
heroList01 的地址是:0x1fab98ec908
originList 第一个元素的地址是:0x1fab86f84b0
heroList01 第一个元素的地址是:0x1fab98f7e10
heroList02 第一个元素的地址是:0x1fab98f7e10
可以看到,当切片运算符[:]出现在赋值运算符=左边时,对列表的修改操作并不会产生新列表,而是在原来的列表上面修改,修改以后,列表中的元素被替换。使用内存图描述如下:
N换三,替换后,列表的英雄数量大于或者小于10个。
originList = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
heroList01 = originList[:]
# 保持原列表不变,在原列表的开头插入新列表
heroList01[:0] = heroList02
print(heroList01)
heroList01 = originList[:]
# 保持原列表不变,在原列表的中间插入新列表
heroList01[4:4] = heroList02
print(heroList01)
heroList01 = originList[:]
# 保持原列表不变,在原列表的末尾插入新列表
heroList01[10:] = heroList02
print(heroList01)
heroList01 = originList[:]
# 1换3
heroList01[:1] = heroList02
print(heroList01)
heroList01 = originList[:]
# 5换3
heroList01[:5] = heroList02
print(heroList01)
heroList01 = originList[:]
# 10换3
heroList01[:] = heroList02
print(heroList01)
上面程序的输出如下:
['幻视', '鹰眼', '蜘蛛侠', '钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['钢铁侠', '美国队长', '雷神', '冬兵', '幻视', '鹰眼', '蜘蛛侠', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神', '幻视', '鹰眼', '蜘蛛侠']
['幻视', '鹰眼', '蜘蛛侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['幻视', '鹰眼', '蜘蛛侠', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
['幻视', '鹰眼', '蜘蛛侠']
进一步对上面程序的列表地址以及各个元素的地址研究后发现,不管是在列表中插入新列表,还是1换3、5换3、10换3,均没有销毁heroList01,而只是简单地将heroList01的列表节点重定向到新的列表元素上,并在重定向的过程中新增或者删除旧的列表节点。
测试自学教程这里获取… https://csx-t231.github.io/