51Testing软件测试论坛
标题: 详解python中的列表切片以及它与浅拷贝的关系(上) [打印本页]
作者: lsekfe 时间: 2022-7-5 11:10
标题: 详解python中的列表切片以及它与浅拷贝的关系(上)
前言
在讲python中的列表之前,首先要引入字符串,在python中,字符串与列表都属于有序容器,有序意味着可以通过索引以及切片的方式对容器中的元素进行定位。另外,字符串与列表的区别在于字符串是不可变容器,意味着不能修改字符串的内容。
字符串的索引与切片
· 字符串索引
在python中,字符串被视为一种不可变序列,序列意味着可以使用索引以及切片来定位字符串中的元素,而不可变则意味着所有对字符串的修改操作都是非法的。
索引是一种定位字符串中单个字符的做法,在python中,索引分为正序索引和逆序索引。而对于字符串这个具体的对象,索引是指字符串中的每个字符在字符串中的位置。正序索引从字符串起始处,从左往右分别给字符串中的字符编号,从0开始依次递增;逆序索引从字符串结尾处,从右往左分别给字符串中的字符编号,从-1开始依次递减。使用(字符串[索引下标])的方法,从字符串中定位单个字符。
举例:对于"一起学习python自动化测试"这个字符串,我要定位它的第一个字符和最后一个字符,则可以采用如下的形式。
- str01 = "一起学习python自动化测试"
- print(str01[0])
- print(str01[-1])
复制代码上面程序的终端输出如下:
注意:索引值不能超过字符串长度,否则会报IndexError
- <font face="微软雅黑">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</font>
复制代码· 字符串切片
利用索引可以定位字符串中单个字符,当需要以某种形式获取字符串的子串时,切片就登场了。切片的语法是:容器[开始索引:结束索引:步长],表示从容器的开始索引处开始,到结束索引处为止,每隔一定的步长取出容器中的元素,并组合成一个新的容器,新容器的类型与原容器相同。这里的容器可以是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)
复制代码上面程序的终端输出分别如下:
- <font size="3" face="微软雅黑">一起学习
- 学习python
- 自动化测试
- 一学
- 学yo
- 习学起一
- 试测化动自</font>
复制代码列表切片
与字符串一样,列表也是python中的一种有序容器,并且容器的内容是可变的,可以使用索引及切片按照指定的规则从容器中获取或修改元素。
假设有这样一个漫威电影宇宙,它有钢铁侠、美国队长、雷神、冬兵、蚁人、浩克、猩红女巫、女武神、星爵、格鲁特等英雄。我们给这个电影宇宙做一个列表,如下
- heroList01 = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
复制代码· 使用切片的方法从容器中获取元素
与字符串操作类似,可以通过切片的方法获取列表中符合切片规则的元素,甚至获取整个列表的元素。列表的切片语法是:容器[开始索引:结束索引:步长]。
o 获取列表中部分元素
与字符串操作类似,我们可以获取列表中的前三个英雄、后五个英雄、第三到第七个英雄、逆序输出前三个/后五个英雄等等。
- # 获取列表中的前三个英雄
- 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)
复制代码上面程序的输出分别是:
- ['钢铁侠', '美国队长', '雷神']
- ['星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
- ['雷神', '冬兵', '浩克', '星爵', '格鲁特']
- ['雷神', '美国队长', '钢铁侠']
- ['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵']
复制代码o 使用切片复制整个列表
同样,我们可以使用切片复制整个列表,并且可以是以顺序或者逆序的方式复制。
- # 通过切片的形式复制列表
- heroList02 = heroList01[::]
- print(heroList02)
- # 通过切片的形式逆序复制列表
- heroList02 = heroList01[::-1]
- print(heroList02)
复制代码上面程序的输出如下:
- ['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
- ['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵', '浩克', '冬兵', '雷神', '美国队长', '钢铁侠']
复制代码使用切片时,python以浅拷贝的方式生成列表,并赋值给新对象。这与列表赋值不同,列表赋值仅仅是简单地将列表的首地址赋值给另外一个变量。我们用内存图的方式来呈现一下列表切片复制的整个过程
[attach]138784[/attach]
· 使用切片的方法修改容器中的元素
由于列表是可变容器,我们可以修改列表中的元素,常规的修改方法是通过索引定位到列表中的某个元素然后加以修改。除此之外,我们可以使用切片的方法对列表中的一片特定区域做修改。
还是以上面的漫威电影宇宙列表为例,我们使用三个新的英雄替换原先的英雄列表,新的英雄分别是幻视、鹰眼、蜘蛛侠。有几种替换方案:
o 三换三,替换后,列表的英雄数量还是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
复制代码可以看到,当切片运算符[:]出现在赋值运算符=左边时,对列表的修改操作并不会产生新列表,而是在原来的列表上面修改,修改以后,列表中的元素被替换。使用内存图描述如下:
[attach]138785[/attach]
o 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的列表节点重定向到新的列表元素上,并在重定向的过程中新增或者删除旧的列表节点。
欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) |
Powered by Discuz! X3.2 |