51Testing软件测试论坛

标题: 详解python中的列表切片以及它与浅拷贝的关系(上) [打印本页]

作者: lsekfe    时间: 2022-7-5 11:10
标题: 详解python中的列表切片以及它与浅拷贝的关系(上)
前言
在讲python中的列表之前,首先要引入字符串,在python中,字符串与列表都属于有序容器,有序意味着可以通过索引以及切片的方式对容器中的元素进行定位。另外,字符串与列表的区别在于字符串是不可变容器,意味着不能修改字符串的内容。
字符串的索引与切片
·      字符串索引
在python中,字符串被视为一种不可变序列,序列意味着可以使用索引以及切片来定位字符串中的元素,而不可变则意味着所有对字符串的修改操作都是非法的。
索引是一种定位字符串中单个字符的做法,在python中,索引分为正序索引和逆序索引。而对于字符串这个具体的对象,索引是指字符串中的每个字符在字符串中的位置。正序索引从字符串起始处,从左往右分别给字符串中的字符编号,从0开始依次递增;逆序索引从字符串结尾处,从右往左分别给字符串中的字符编号,从-1开始依次递减。使用(字符串[索引下标])的方法,从字符串中定位单个字符。
举例:对于"一起学习python自动化测试"这个字符串,我要定位它的第一个字符和最后一个字符,则可以采用如下的形式。
  1. str01 = "一起学习python自动化测试"
  2. print(str01[0])               
  3. print(str01[-1])
复制代码
上面程序的终端输出如下:


复制代码
注意:索引值不能超过字符串长度,否则会报IndexError
  1. <font face="微软雅黑">In [7]: print(str01[15])
  2. ---------------------------------------------------------------------------
  3. IndexError                                Traceback (most recent call last)
  4. <ipython-input-7-92262652de33> in <module>
  5. ----> 1 print(str01[15])

  6. IndexError: string index out of range</font>
复制代码
·      字符串切片
利用索引可以定位字符串中单个字符,当需要以某种形式获取字符串的子串时,切片就登场了。切片的语法是:容器[开始索引:结束索引:步长],表示从容器的开始索引处开始,到结束索引处为止,每隔一定的步长取出容器中的元素,并组合成一个新的容器,新容器的类型与原容器相同。这里的容器可以是python中任意类型的有序容器,包括字符串、列表、元组等等。开始索引默认为0,步长默认为1,如果不指定结束索引,则默认索引到容器的最后一个元素。步长为负数,表示从字符串末尾往前索引(逆序)。
还是以上面索引的字符串为例,使用切片获取字符串的子串,用法如下:
  1. str01 = "一起学习python自动化测试"
  2. #   获取字符串的前4个字符
  3. str02 = str01[:4:]
  4. print(str02)
  5. #   获取字符串的第3到第10个字符
  6. str02 = str01[2:10:]
  7. print(str02)
  8. #        获取字符串的末尾5个字符
  9. str02 = str01[-5::]
  10. print(str02)
  11. #        获取字符串的前4个字符,步长为2
  12. str02 = str01[:4:2]
  13. print(str02)
  14. #        获取字符串的第3到第10个字符,步长为3
  15. str02 = str01[2:10:3]
  16. print(str02)
  17. #        逆序输出字符串的前4个字符
  18. str02 = str01[-12::-1]
  19. print(str02)
  20. #        逆序输出字符串的末尾5个字符
  21. str02 = str01[:-6:-1]
  22. print(str02)
复制代码
上面程序的终端输出分别如下:
  1. <font size="3" face="微软雅黑">一起学习
  2. 学习python
  3. 自动化测试
  4. 一学
  5. 学yo
  6. 习学起一
  7. 试测化动自</font>
复制代码
列表切片
与字符串一样,列表也是python中的一种有序容器,并且容器的内容是可变的,可以使用索引及切片按照指定的规则从容器中获取或修改元素。

假设有这样一个漫威电影宇宙,它有钢铁侠、美国队长、雷神、冬兵、蚁人、浩克、猩红女巫、女武神、星爵、格鲁特等英雄。我们给这个电影宇宙做一个列表,如下
  1. heroList01 = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
复制代码
·      使用切片的方法从容器中获取元素
与字符串操作类似,可以通过切片的方法获取列表中符合切片规则的元素,甚至获取整个列表的元素。列表的切片语法是:容器[开始索引:结束索引:步长]。
o  获取列表中部分元素

与字符串操作类似,我们可以获取列表中的前三个英雄、后五个英雄、第三到第七个英雄、逆序输出前三个/后五个英雄等等。
  1. #        获取列表中的前三个英雄
  2. heroList02 = heroList01[:3:]
  3. print(heroList02)
  4. #        获取列表中的后五个英雄
  5. heroList02 = heroList01[-5::]
  6. print(heroList02)
  7. #        获取列表的第三到第七个英雄
  8. heroList02 = heroList01[2:7:]
  9. print(heroList02)
  10. #        逆序输出列表前三个英雄
  11. heroList02 = heroList01[-8::-1]
  12. print(heroList02)
  13. #        逆序输出列表后五个英雄
  14. heroList02 = heroList01[:-6:-1]
  15. print(heroList02)
复制代码
上面程序的输出分别是:
  1. ['钢铁侠', '美国队长', '雷神']
  2. ['星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  3. ['雷神', '冬兵', '浩克', '星爵', '格鲁特']
  4. ['雷神', '美国队长', '钢铁侠']
  5. ['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵']
复制代码
o  使用切片复制整个列表

同样,我们可以使用切片复制整个列表,并且可以是以顺序或者逆序的方式复制。
  1. #        通过切片的形式复制列表
  2. heroList02 = heroList01[::]
  3. print(heroList02)
  4. #        通过切片的形式逆序复制列表
  5. heroList02 = heroList01[::-1]
  6. print(heroList02)
复制代码
上面程序的输出如下:
  1. ['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  2. ['女武神', '猩红女巫', '蚁人', '格鲁特', '星爵', '浩克', '冬兵', '雷神', '美国队长', '钢铁侠']
复制代码
使用切片时,python以浅拷贝的方式生成列表,并赋值给新对象。这与列表赋值不同,列表赋值仅仅是简单地将列表的首地址赋值给另外一个变量。我们用内存图的方式来呈现一下列表切片复制的整个过程
[attach]138784[/attach]
·      使用切片的方法修改容器中的元素
由于列表是可变容器,我们可以修改列表中的元素,常规的修改方法是通过索引定位到列表中的某个元素然后加以修改。除此之外,我们可以使用切片的方法对列表中的一片特定区域做修改。
还是以上面的漫威电影宇宙列表为例,我们使用三个新的英雄替换原先的英雄列表,新的英雄分别是幻视、鹰眼、蜘蛛侠。有几种替换方案:

o  三换三,替换后,列表的英雄数量还是10个。
  1. originList = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
  2. heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
  3. heroList01 = originList[:]
  4. #        使用切片的方式替换heroList01的前三个元素
  5. heroList01[:3] = heroList02
  6. print(heroList01)
  7. heroList01 = originList[:]
  8. #        使用切片的方式替换heroList01的第5-7个元素
  9. heroList01[4:7] = heroList02
  10. print(heroList01)
  11. heroList01 = originList[:]
  12. #        使用切片的方式替换heroList01的末尾三个元素
  13. heroList01[-3:] = heroList02
  14. print(heroList01)
  15. heroList01 = originList[:]
  16. #        使用切片的方式替换heroList01的末尾三个元素,并且是逆序
  17. heroList01[:-4:-1] = heroList02
  18. print(heroList01)
  19. heroList01 = originList[:]
  20. #        使用切片的方式替换heroList01的前三个元素,并且是逆序
  21. heroList01[-8::-1] = heroList02
  22. print(heroList01)
复制代码
上面程序的输出如下:
  1. ['幻视', '鹰眼', '蜘蛛侠', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  2. ['钢铁侠', '美国队长', '雷神', '冬兵', '幻视', '鹰眼', '蜘蛛侠', '蚁人', '猩红女巫', '女武神']
  3. ['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '幻视', '鹰眼', '蜘蛛侠']
  4. ['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蜘蛛侠', '鹰眼', '幻视']
  5. ['蜘蛛侠', '鹰眼', '幻视', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
复制代码
由上面程序输出可以看出,使用切片的方式可以将列表1的部分元素批量替换成列表2的元素。我们以替换前三个元素为例,研究一下切片过程中的内存布局变化
  1. heroList01 = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
  2. heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
  3. print("heroList01 的内容是:{}".format(heroList01))
  4. print("heroList01 的地址是:{}".format(hex(id(heroList01))))
  5. print("originList 第一个元素的地址是:{}".format(hex(id(originList[0]))))
  6. print("heroList01 第一个元素的地址是:{}".format(hex(id(heroList01[0]))))
  7. print("heroList02 第一个元素的地址是:{}".format(hex(id(heroList02[0]))))
  8. heroList01[:3] = heroList02
  9. print("heroList01 的内容是:{}".format(heroList01))
  10. print("heroList01 的地址是:{}".format(hex(id(heroList01))))
  11. print("originList 第一个元素的地址是:{}".format(hex(id(originList[0]))))
  12. print("heroList01 第一个元素的地址是:{}".format(hex(id(heroList01[0]))))
  13. print("heroList02 第一个元素的地址是:{}".format(hex(id(heroList02[0]))))
复制代码
上面程序的输出如下:
  1. heroList01 的内容是:['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  2. heroList01 的地址是:0x1fab98ec908
  3. originList 第一个元素的地址是:0x1fab86f84b0
  4. heroList01 第一个元素的地址是:0x1fab86f84b0
  5. heroList02 第一个元素的地址是:0x1fab98f7e10
  6. heroList01 的内容是:['幻视', '鹰眼', '蜘蛛侠', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  7. heroList01 的地址是:0x1fab98ec908
  8. originList 第一个元素的地址是:0x1fab86f84b0
  9. heroList01 第一个元素的地址是:0x1fab98f7e10
  10. heroList02 第一个元素的地址是:0x1fab98f7e10
复制代码
可以看到,当切片运算符[:]出现在赋值运算符=左边时,对列表的修改操作并不会产生新列表,而是在原来的列表上面修改,修改以后,列表中的元素被替换。使用内存图描述如下:
[attach]138785[/attach]
o  N换三,替换后,列表的英雄数量大于或者小于10个。
  1. originList = ["钢铁侠", "美国队长", "雷神", "冬兵", "浩克", "星爵", "格鲁特", "蚁人", "猩红女巫", "女武神"]
  2. heroList02 = ["幻视", "鹰眼", "蜘蛛侠"]
  3. heroList01 = originList[:]
  4. #        保持原列表不变,在原列表的开头插入新列表
  5. heroList01[:0] = heroList02
  6. print(heroList01)
  7. heroList01 = originList[:]
  8. #        保持原列表不变,在原列表的中间插入新列表
  9. heroList01[4:4] = heroList02
  10. print(heroList01)
  11. heroList01 = originList[:]
  12. #        保持原列表不变,在原列表的末尾插入新列表
  13. heroList01[10:] = heroList02
  14. print(heroList01)
  15. heroList01 = originList[:]
  16. #        1换3
  17. heroList01[:1] = heroList02
  18. print(heroList01)
  19. heroList01 = originList[:]
  20. #        5换3
  21. heroList01[:5] = heroList02
  22. print(heroList01)
  23. heroList01 = originList[:]
  24. #        10换3
  25. heroList01[:] = heroList02
  26. print(heroList01)
复制代码
上面程序的输出如下:
  1. ['幻视', '鹰眼', '蜘蛛侠', '钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  2. ['钢铁侠', '美国队长', '雷神', '冬兵', '幻视', '鹰眼', '蜘蛛侠', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  3. ['钢铁侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神', '幻视', '鹰眼', '蜘蛛侠']
  4. ['幻视', '鹰眼', '蜘蛛侠', '美国队长', '雷神', '冬兵', '浩克', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  5. ['幻视', '鹰眼', '蜘蛛侠', '星爵', '格鲁特', '蚁人', '猩红女巫', '女武神']
  6. ['幻视', '鹰眼', '蜘蛛侠']
复制代码
进一步对上面程序的列表地址以及各个元素的地址研究后发现,不管是在列表中插入新列表,还是1换3、5换3、10换3,均没有销毁heroList01,而只是简单地将heroList01的列表节点重定向到新的列表元素上,并在重定向的过程中新增或者删除旧的列表节点。














欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2