一、创建一个列表,并用索引访问列表中的元素
>>> [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
>>> [1, 2, 3, 4, 5, "上山打老虎"]
[1, 2, 3, 4, 5, '上山打老虎']
>>> rhyme = [1, 2, 3, 4, 5, "上山打老虎"]
>>> print(rhyme)
[1, 2, 3, 4, 5, '上山打老虎']
>>> for each in rhyme:
print(each)
1
2
3
4
5
上山打老虎
>>> rhyme[0]
1
>>> rhyme[1]
2
>>> rhyme[5]
'上山打老虎'
>>> rhyme[-1]
'上山打老虎'
>>> rhyme[-2]
5
>>> rhyme[-3]
4
>>> length = len(rhyme)
>>> print(length)
6
>>> rhyme[length-1]
'上山打老虎'
>>>
二、列表切片
>>> rhyme[0:3]
[1, 2, 3]
>>> rhyme[3:6]
[4, 5, '上山打老虎']
>>> rhyme[:3]
[1, 2, 3]
>>> rhyme[3:]
[4, 5, '上山打老虎']
>>> rhyme[:]
[1, 2, 3, 4, 5, '上山打老虎']
>>> rhyme[0:6:2]
[1, 3, 5]
>>> rhyme[::2]
[1, 3, 5]
>>> rhyme[::-2]
['上山打老虎', 4, 2]
>>> rhyme[::-1]
['上山打老虎', 5, 4, 3, 2, 1]
>>>
三、列表的诸多方法(增、删、改、查)
1.向列表中添加元素:
(一)在列表末尾添加元素
(1)append()方法,是在列表的末尾添加一个指定的元素;缺点:每次只能添加一个元素。
>>> heros = ["钢铁侠", "绿巨人"]
>>> heros.append("黑寡妇")
>>> heros
['钢铁侠', '绿巨人', '黑寡妇']
>>>
(2)extend()方法:允许在列表的末尾直接添加一个可迭代对象。
但要注意的是:
>>> heros.extend(["鹰眼", "灭霸", "雷神"])
>>> heros
['钢铁侠', '绿巨人', '黑寡妇', '鹰眼', '灭霸', '雷神']
>>>
(3)用切片方法在列表末尾添加元素:无切片,不python;如果说发量是检验程序猿水平的标准,那么切片就是检验一个python程序猿是否深刻掌握python的标准。
>>> s = [1, 2, 3, 4, 5]
>>> s[len(s):] = [6] # 相当于 s.append(6)
>>> s
[1, 2, 3, 4, 5, 6]
>>> s[len(s):] = [7, 8, 9] # 相当于 s.extend([7, 8, 9])
>>> s
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
(二)在列表任意位置添加元素:insert()方法
insert()方法:insert(待插入的位置, 待插入的元素)
>>> s = [1, 3, 4, 5]
>>> s.insert(1, 2)
>>> s
[1, 2, 3, 4, 5]
>>> s.insert(0, 0)
>>> s
[0, 1, 2, 3, 4, 5]
>>> s.insert(0, "python")
>>> s
['python', 0, 1, 2, 3, 4, 5]
>>> s.insert(len(s), 6)
>>> s
['python', 0, 1, 2, 3, 4, 5, 6]
>>> s.insert(-1, "FishC")
>>> s
['python', 0, 1, 2, 3, 4, 5, 'FishC', 6]
>>> s.insert(-2, "love")
>>> s
['python', 0, 1, 2, 3, 4, 5, 'love', 'FishC', 6]
>>>
insert(i,e)是在下标为i的地方添加e。
举个例子一个列表长度5,insert(-1,e)中的-1其实应该理解为在第5个位置也就是下标为4的地方添加e,然后原来第5个位置的元素移到了第6个位置,所以看起来是加在了倒数第二个。
你排队插进去,那后面的人都要后移就是这个道理。
2.删除列表中的元素
(一)将列表中指定元素删除:remove()方法
>>> heros
['钢铁侠', '绿巨人', '黑寡妇', '鹰眼', '灭霸', '雷神']
>>> heros.remove("灭霸")
>>> heros
['钢铁侠', '绿巨人', '黑寡妇', '鹰眼', '雷神']
>>> heros.remove("金莲")
Traceback (most recent call last):
File "<pyshell#81>", line 1, in <module>
heros.remove("金莲")
ValueError: list.remove(x): x not in list
>>>
需要注意的是:
(二)删除列表某个位置上的元素:pop()方法
pop()方法:参数为元素的下标索引值。
>>> heros
['钢铁侠', '绿巨人', '黑寡妇', '鹰眼', '雷神']
>>> heros.pop(2)
'黑寡妇'
>>> heros
['钢铁侠', '绿巨人', '鹰眼', '雷神']
>>>
(三)清空列表元素:clear()方法
>>> heros
['钢铁侠', '绿巨人', '鹰眼', '雷神']
>>> heros.clear()
>>> heros
[]
>>>
3.替换列表中的元素
列表和字符串最大的区别就是,列表是可变的,而字符串是不可变的。
替换列表中的元素实际和访问列表中的元素类似,都是使用下标索引的方法,然后用赋值运算符将新的元素给替换进去。
(一)替换列表中某个元素:使用下标
>>> heros = ["蜘蛛侠", "绿巨人", "黑寡妇", "鹰眼", "灭霸", "雷神"]
>>> heros[4] = "钢铁侠"
>>> heros
['蜘蛛侠', '绿巨人', '黑寡妇', '鹰眼', '钢铁侠', '雷神']
>>>
(二)替换列表中多个连续元素:使用切片
示例1:替换列表中多个连续元素
>>> heros
['蜘蛛侠', '绿巨人', '黑寡妇', '鹰眼', '钢铁侠', '雷神']
>>> heros[3:] = ["武松", "林冲", "李逵"]
>>> heros
['蜘蛛侠', '绿巨人', '黑寡妇', '武松', '林冲', '李逵']
>>>
示例2:翻转列表中的元素
方法1:
>>> nums = [3, 1, 9, 6, 8, 3, 5, 3]
>>> nums.sort()
>>> nums
[1, 3, 3, 3, 5, 6, 8, 9]
>>> nums.reverse()
>>> nums
[9, 8, 6, 5, 3, 3, 3, 1]
>>> heros.reverse()
>>> heros
['李逵', '林冲', '武松', '黑寡妇', '绿巨人', '蜘蛛侠']
方法2:
>>> nums = [3, 1, 9, 6, 8, 3, 5, 3]
>>> nums.sort(reverse=True)
>>> nums
[9, 8, 6, 5, 3, 3, 3, 1]
>>>
4.查询列表中的元素
(一)查找列表中某个元素出现的次数:count()方法
>>> nums
[9, 8, 6, 5, 3, 3, 3, 1]
>>> nums.count(3)
3
>>>
(二)查查找列表中某个元素的索引值:index()方法
>>> heros
['李逵', '林冲', '武松', '黑寡妇', '绿巨人', '蜘蛛侠']
>>> heros.index("绿巨人")
4
当需要将列表中某个元素替换成别的元素且又不知道索引时,就可以用到index()方法,示例如下:
>>> heros[heros.index("绿巨人")] = "神奇女侠"
>>> heros
['李逵', '林冲', '武松', '黑寡妇', '神奇女侠', '蜘蛛侠']
>>>
当列表中有多个相同的元素时,index()方法返回的是第一个找到的元素下标值,且 index(x, start, end) 有两个可选参数,用来指定查找的开始和结束位置(下标值),示例如下:
>>> nums = [3, 1, 9, 6, 8, 3, 5, 3]
>>> nums.index(3)
0
>>> nums.index(3, 1, 7)
5
>>>
5. 拷贝列表
(一).copy()方法(浅拷贝)
(二).切片方法(浅拷贝)
>>> nums = [3, 1, 9, 6, 8, 3, 5, 3]
>>> nums_copy1 = nums.copy()
>>> nums_copy1
[3, 1, 9, 6, 8, 3, 5, 3]
>>> nums_copy2 = nums[:]
>>> nums_copy2
[3, 1, 9, 6, 8, 3, 5, 3]
>>>
6.列表的加法与乘法
>>> s = [1, 2, 3]
>>> t = [4, 5, 6]
>>> s + t
[1, 2, 3, 4, 5, 6]
>>> s * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>>
7.总结:列表诸多方法大合集:
四、嵌套列表
1.嵌套列表的两种写法
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
>>> matrix
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2.访问嵌套列表
>>> for i in matrix:
for each in i:
print(each)
1
2
3
4
5
6
7
8
9
>>> for i in matrix:
for each in i:
print(each, end=' ')
print()
1 2 3
4 5 6
7 8 9
>>> matrix[0]
[1, 2, 3]
>>> matrix[0][0]
1
>>> matrix[1][1]
5
>>> matrix[2][2]
9
>>>
3.使用循环语句创建并初始化二维列表
正确示例如下:
>>> A = [0] * 3
>>> A
[0, 0, 0]
>>> for i in range(3):
A[i] = [0] * 3
>>> A
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>>
注意:一种高级的错误写法如下:
>>> B = [[0] *3] * 3
>>> B
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>>
错误原因可以从如下得以窥见:
>>> A[1][1] = 1
>>> A
[[0, 0, 0], [0, 1, 0], [0, 0, 0]]
>>> B[1][1] = 1
>>> B
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
>>>
可以看到,我们改的只是一个元素,但是这里B修改后的结果却影响了每一行的对应列上的元素,想要搞清原因这里我们引入 is 运算符,它用于检验两个变量是否指向同一个对象的一个运算符,用法如下示例:
>>> x = "FishC"
>>> y = "FishC"
>>> x is y
True
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x is y
False
>>>
这说明,python对于不同对象的存储机制是不一样的,"FishC"是一个字符串,而字符串是不可变的,在内存中只需要开辟一个位置来存放就可以,如果有多个变量名指向该字符串,比如这里的x 和y都指向"FishC",那它们的关系就是如下:
而列表是可变的,所以尽管两个列表的内容是一样的,但是python还是需要为它们开辟两个不同的位置来进行存储,如下所示:
因此我们可以用 is运算符 来进行验证:
>>> A[0] is A[1]
False
>>> A[1] is A[2]
False
>>> B[0] is B[1]
True
>>> B[1] is B[2]
True
>>>
可以看到的是,列表A的第一二三行是三个不同的对象,而B的第一二三行实则是同一对象。
列表A、B在内存中的布局分别如下所示:
B的错因就在于,试图通过乘号对一个嵌套列表进行拷贝,但其实呢,它拷贝的只是对同一个列表的引用,就像这里我们展示的内存布局一样。
>>> C = [[0] * 3]
>>> C
[[0, 0, 0]]
>>>
其实,这个问题在python的官方文档中也进行了强调:
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]
>>>
五、浅拷贝(Shallow Copy)和深拷贝(Deep Copy)
在Python中,理解浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是非常重要的,特别是在处理复杂的数据结构(如列表、字典等)时。浅拷贝和深拷贝的主要区别在于它们如何处理对象中的可变元素。
1.概念
(一)浅拷贝(Shallow Copy)
浅拷贝创建了一个新的复合对象,然后(到可能的最顶层)插入原对象的元素的引用。这意味着,如果原对象的元素是不可变的(如整数、字符串、元组等),那么浅拷贝后的对象中的这些元素与原对象中的元素是完全独立的;但如果原对象的元素是可变的(如列表、字典等),那么浅拷贝后的对象中的这些元素仅仅是原对象中的元素的引用,它们指向同一个内存地址。
Python中,可以使用copy
模块的copy()
函数来实现浅拷贝。
(二)深拷贝(Deep Copy)
深拷贝会递归地复制对象中的所有内容,包括对象中的可变元素。这意呀着,深拷贝后的对象与原对象是完全独立的,修改原对象中的元素不会影响深拷贝后的对象。
Python中,可以使用copy
模块的deepcopy()
函数来实现深拷贝。
下面我将再用Python代码示例来详细解释浅拷贝(Shallow Copy)和深拷贝(Deep Copy)的区别。
2.代码示例
(一)浅拷贝示例
在这个示例中,我们将创建一个包含整数和列表的列表,然后对这个列表进行浅拷贝。之后,我们将修改原始列表中嵌套列表的一个元素,并观察这个修改是否影响到了浅拷贝后的列表。
import copy
# 原始列表
original_list = [1, 2, [3, 4], 5]
# 浅拷贝
shallow_copied_list = copy.copy(original_list)
# 打印浅拷贝前的两个列表(此时它们看起来是相同的)
print("原始列表:", original_list)
print("浅拷贝后的列表:", shallow_copied_list)
# 修改原始列表中嵌套列表的一个元素
original_list[2][0] = 'a'
# 打印修改后的两个列表(观察浅拷贝后的列表是否也被修改了)
print("修改后的原始列表:", original_list)
print("修改后的浅拷贝列表:", shallow_copied_list) # 这里的嵌套列表也会被修改,因为浅拷贝只复制了最外层
输出将会是:
原始列表: [1, 2, [3, 4], 5]
浅拷贝后的列表: [1, 2, [3, 4], 5]
修改后的原始列表: [1, 2, ['a', 4], 5]
修改后的浅拷贝列表: [1, 2, ['a', 4], 5] # 浅拷贝列表中的嵌套列表也被修改了
(二)深拷贝示例
在这个示例中,我们将使用与上面相同的原始列表,但这次我们将对它进行深拷贝。之后,我们再次修改原始列表中嵌套列表的一个元素,并观察这个修改是否影响到了深拷贝后的列表。
import copy
# 原始列表
original_list = [1, 2, [3, 4], 5]
# 深拷贝
deep_copied_list = copy.deepcopy(original_list)
# 打印深拷贝前的两个列表(此时它们看起来是相同的)
print("原始列表:", original_list)
print("深拷贝后的列表:", deep_copied_list)
# 修改原始列表中嵌套列表的一个元素
original_list[2][0] = 'a'
# 打印修改后的两个列表(观察深拷贝后的列表是否也被修改了)
print("修改后的原始列表:", original_list)
print("修改后的深拷贝列表:", deep_copied_list) # 这里的嵌套列表不会被修改,因为深拷贝复制了所有层
输出将会是:
原始列表: [1, 2, [3, 4], 5]
深拷贝后的列表: [1, 2, [3, 4], 5]
修改后的原始列表: [1, 2, ['a', 4], 5]
修改后的深拷贝列表: [1, 2, [3, 4], 5] # 深拷贝列表中的嵌套列表没有被修改
通过这些示例,能够清楚地看到浅拷贝和深拷贝之间的主要区别:浅拷贝只复制对象本身而不复制对象中的可变元素(如列表、字典等),而深拷贝则递归地复制对象中的所有内容,包括对象中的可变元素。
3.答疑与总结
那可能还会有疑惑,为什么是浅拷贝跟着变,反而深拷贝不跟着变?
浅拷贝和深拷贝之所以在修改原始对象时表现出不同的行为,主要原因在于它们复制对象内容的深度和方式的不同。
(一)浅拷贝
浅拷贝(Shallow Copy)在复制对象时,会创建一个新的对象,并将原始对象中的元素(包括不可变对象和可变对象的引用)复制到新对象中。然而,对于原始对象中的可变元素(如列表、字典等),浅拷贝只复制了这些可变元素的引用,而不是它们的实际内容。因此,如果原始对象中的可变元素被修改,那么由于浅拷贝中的对应元素实际上是指向同一个可变对象的引用,所以浅拷贝中的这些元素也会受到影响,表现出“跟着变”的现象。
(二)深拷贝
深拷贝(Deep Copy)则不同,它在复制对象时,会递归地复制原始对象中的所有内容,包括所有子对象。这意味着深拷贝会创建新的对象,并将原始对象中的所有元素(包括所有子对象)的实际内容复制到新对象中,而不是仅仅复制引用。因此,即使修改了原始对象中的可变元素,深拷贝中的对应元素也不会受到影响,因为它们已经是完全独立的副本了。
(三)总结
- 浅拷贝:只复制对象本身和对象中的不可变元素,对于可变元素只复制引用,不复制实际内容。因此,如果修改了原始对象中的可变元素,浅拷贝中的对应元素也会受到影响。
- 深拷贝:递归地复制对象中的所有内容,包括所有子对象。因此,即使修改了原始对象中的任何元素,深拷贝中的对应元素也不会受到影响,因为它们是独立的副本。
这种差异使得深拷贝和浅拷贝在不同的应用场景中具有不同的优势和局限性。例如,在处理复杂的数据结构或需要确保数据独立性的情况下,深拷贝通常是更好的选择;而在处理简单数据结构或追求性能时,浅拷贝可能更为合适。
4. 运用
>>> x = [1, 2, 3]
>>> y = x
>>> x[1] = 1
>>> x
[1, 1, 3]
>>> y
[1, 1, 3]
>>>
在 Python 中,当你执行 y = x.copy()
并且 x
是一个列表(list)时,这里的 .copy()
方法实际上执行的是浅拷贝(Shallow Copy)。这是因为列表的 .copy()
方法默认只复制列表本身这一层,而不会递归地复制列表中的可变元素(如列表、字典等)的内容。
换句话说,y = x.copy()
创建了一个新的列表 y
,这个新列表包含了与原始列表 x
相同的元素引用(如果元素是可变的),但 y
和 x
是两个不同的列表对象。因此,如果你修改了 x
中的不可变元素(如整数、浮点数、字符串或元组),这些修改不会影响到 y
。但是,如果你修改了 x
中的可变元素(如列表中的另一个列表),并且这个修改改变了元素的内容(而不是替换了整个元素),那么 y
中的对应元素也会受到影响,因为它们指向的是同一个可变对象。
为了说明这一点,我们可以看一个例子:
x = [1, 2, [3, 4]] # x 是一个包含整数和列表的列表
y = x.copy() # y 是 x 的浅拷贝
# 修改 x 中的不可变元素(整数)
x[0] = 99
print(x) # 输出: [99, 2, [3, 4]]
print(y) # 输出: [1, 2, [3, 4]],y 不受影响
# 修改 x 中的可变元素(列表)的一个元素
x[2][0] = 'a'
print(x) # 输出: [99, 2, ['a', 4]]
print(y) # 输出: [1, 2, ['a', 4]],y 中的对应元素也受到影响,因为它们是同一个列表的引用
如你所见,在修改 x
中的不可变元素时,y
没有受到影响。但是,在修改 x
中的可变元素(即列表中的列表)时,y
中的对应元素也受到了影响,因为它们指向的是同一个列表对象。这证明了 .copy()
方法执行的是浅拷贝。
如果你想要执行深拷贝(即递归地复制列表中的所有内容,包括子列表),你应该使用 copy
模块的 deepcopy()
函数,如下所示:
import copy
x = [1, 2, [3, 4]]
y = copy.deepcopy(x) # y 是 x 的深拷贝
# 修改 x 中的可变元素
x[2][0] = 'a'
print(x) # 输出: [1, 2, ['a', 4]]
print(y) # 输出: [1, 2, [3, 4]],y 不受影响,因为 y 中的子列表是 x 中子列表的副本