详解Python中列表切片及浅拷贝的关系(上)

2778 篇文章 17 订阅
2704 篇文章 27 订阅
本文深入探讨Python中的字符串和列表的索引与切片操作。讲解了如何通过索引定位单个字符或子串,以及如何使用切片获取或修改字符串和列表的部分内容。内容涵盖正序和逆序索引,以及切片的语法和应用场景,包括替换、插入和删除元素等。同时,通过实例分析了列表切片时内存布局的变化。
摘要由CSDN通过智能技术生成

前言

在讲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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值