06 - 列表与元组列表大概是 Python 里用得最多的数据结构了。元组跟它很像但有个关键区别。这章把两个放一起讲。列表是什么列表就是一个有序的容器里面可以放任何东西而且可以随时增删改。fruits[苹果,香蕉,橘子]numbers[1,2,3,4,5]mixed[1,hello,True,3.14]# 什么类型都能混着放empty[]# 空列表用方括号[]创建元素之间用逗号隔开。访问元素跟字符串一样用索引和切片fruits[苹果,香蕉,橘子,葡萄]print(fruits[0])# 苹果print(fruits[-1])# 葡萄print(fruits[1:3])# [香蕉, 橘子]print(fruits[::-1])# [葡萄, 橘子, 香蕉, 苹果]列表长度用len()print(len(fruits))# 4修改列表字符串不能改但列表可以。改元素fruits[苹果,香蕉,橘子]fruits[1]芒果print(fruits)# [苹果, 芒果, 橘子]添加元素fruits[苹果,香蕉]# append在末尾加一个fruits.append(橘子)print(fruits)# [苹果, 香蕉, 橘子]# insert在指定位置插入fruits.insert(1,芒果)print(fruits)# [苹果, 芒果, 香蕉, 橘子]# extend把另一个列表的元素加进来more[葡萄,西瓜]fruits.extend(more)print(fruits)# [苹果, 芒果, 香蕉, 橘子, 葡萄, 西瓜]append和extend的区别新手容易搞混a[1,2,3]a.append([4,5])# [1, 2, 3, [4, 5]]把整个列表当成一个元素加进去a.extend([4,5])# [1, 2, 3, 4, 5]把里面的元素一个个加进去也可以用运算符a[1,2,3]b[4,5,6]cabprint(c)# [1, 2, 3, 4, 5, 6]删除元素fruits[苹果,香蕉,橘子,香蕉]# remove按值删除只删第一个匹配的fruits.remove(香蕉)print(fruits)# [苹果, 橘子, 香蕉]# pop按索引删除并返回默认删最后一个fruits[苹果,香蕉,橘子]lastfruits.pop()print(last)# 橘子print(fruits)# [苹果, 香蕉]secondfruits.pop(1)# 删索引 1print(second)# 香蕉# del也可以按索引删fruits[苹果,香蕉,橘子]delfruits[1]print(fruits)# [苹果, 橘子]# clear清空整个列表fruits.clear()print(fruits)# []remove如果找不到要删的值会报错所以用的时候最好先确认一下在不在if香蕉infruits:fruits.remove(香蕉)列表的常用操作排序numbers[3,1,4,1,5,9,2,6]# sort()原地排序直接修改原列表numbers.sort()print(numbers)# [1, 1, 2, 3, 4, 5, 6, 9]# 降序numbers.sort(reverseTrue)print(numbers)# [9, 6, 5, 4, 3, 2, 1, 1]# sorted()不修改原列表返回新的排序列表numbers[3,1,4]new_listsorted(numbers)print(numbers)# [3, 1, 4]没变print(new_list)# [1, 3, 4]sort()和sorted()的区别一个改原来的一个创建新的。看你的需求选。还可以自定义排序规则words[banana,apple,cherry]words.sort(keylen)# 按长度排序print(words)# [apple, banana, cherry]查找fruits[苹果,香蕉,橘子,香蕉]# index()找某个值的索引只返回第一个print(fruits.index(香蕉))# 1# count()某个值出现了几次print(fruits.count(香蕉))# 2# in在不在里面print(苹果infruits)# True反转numbers[1,2,3,4,5]numbers.reverse()print(numbers)# [5, 4, 3, 2, 1]复制列表这里有个坑要注意a[1,2,3]ba# 这不是复制b 和 a 指向同一个列表b.append(4)print(a)# [1, 2, 3, 4]a 也变了正确复制的方式a[1,2,3]ba.copy()# 方法一copy()ba[:]# 方法二切片blist(a)# 方法三list()b.append(4)print(a)# [1, 2, 3]没变print(b)# [1, 2, 3, 4]但这些都是浅拷贝。如果列表里有嵌套的可变对象里面的对象还是共享的a[[1,2],[3,4]]ba.copy()b[0].append(99)print(a)# [[1, 2, 99], [3, 4]]里面的列表还是被改了如果需要完全的深拷贝importcopy a[[1,2],[3,4]]bcopy.deepcopy(a)b[0].append(99)print(a)# [[1, 2], [3, 4]]完全独立了列表推导式预告这个后面第 17 章会详细讲这里先见识一下# 生成 1-10 的平方squares[x**2forxinrange(1,11)]print(squares)# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]# 过滤偶数evens[xforxinrange(1,11)ifx%20]print(evens)# [2, 4, 6, 8, 10]一行顶一个循环Python 的招牌特性之一。元组元组跟列表几乎一样区别就一个元组不能改。用圆括号()创建point(3,4)colors(红,绿,蓝)empty()# 空元组single(42,)# 只有一个元素的元组注意逗号不能少那个逗号很重要。(42)不加逗号的话 Python 会以为你在写数学表达式结果就是数字42不是元组。元组能做什么索引和切片跟列表一样point(3,4)print(point[0])# 3print(point[-1])# 4不能改point(3,4)# point[0] 5 # TypeError!那为什么要用元组我当初学的时候就在想列表能改元组不能改那直接用列表不就好了后来发现元组有几个不可替代的地方安全。你不想让别人改的数据就用元组比如坐标、颜色值这些一旦确定就不该变的东西。可以做字典的 key。列表不行因为列表可变元组可以。解包。这个是元组最帅的用法# 解包x,y(3,4)print(x,y)# 3 4# 函数返回多个值其实就是返回元组defget_position():return3,4# 等价于 return (3, 4)x,yget_position()性能。元组比列表占内存少创建速度也稍微快一点。差别不大但在大数据量的时候有感知。命名元组如果你觉得(3, 4)这种写法不够直观不知道每个位置是什么意思可以用命名元组fromcollectionsimportnamedtuple Pointnamedtuple(Point,[x,y])pPoint(3,4)print(p.x,p.y)# 3 4看着像对象但本质还是元组不可变。在定义简单的数据容器时很好用。深拷贝与浅拷贝前面简单提过这个坑这里展开讲讲。Python 里复制对象有两种方式区别很大。浅拷贝Shallow Copy浅拷贝创建一个新对象但里面的元素还是引用原来的importcopy a[[1,2],[3,4]]ba.copy()# 浅拷贝# b a[:] # 这也是浅拷贝# b list(a) # 这也是浅拷贝# b copy.copy(a) # 这还是浅拷贝b[0].append(99)print(a)# [[1, 2, 99], [3, 4]] ← 里面的列表还是被改了为什么因为浅拷贝只是复制了外层列表里面的[1, 2]和[3, 4]这两个列表对象还是共享的。画个图就是a → [ list1, list2 ] ↑ ↑ b → [ list1, list2 ] list1 和 list2 是同一个对象深拷贝Deep Copy深拷贝会递归地复制所有层完全独立importcopy a[[1,2],[3,4]]bcopy.deepcopy(a)# 深拷贝b[0].append(99)print(a)# [[1, 2], [3, 4]] ← 完全不受影响print(b)# [[1, 2, 99], [3, 4]]a → [ list1, list2 ] b → [ list3, list4 ] 全新的对象什么时候用哪个场景用什么列表里都是不可变对象数字、字符串浅拷贝就够了列表里有嵌套的可变对象列表、字典需要深拷贝不确定用深拷贝保险# 全是数字浅拷贝没问题a[1,2,3]ba.copy()b.append(4)print(a)# [1, 2, 3] 不受影响# 有嵌套列表浅拷贝会出问题a[{name:小明},{name:小红}]ba.copy()b[0][name]小刚print(a[0][name])# 小刚 ← 被改了注意一个细节如果嵌套的是不可变对象浅拷贝也安全a[(1,2),(3,4)]# 里面是元组不可变ba.copy()# b[0] (99, 99) # 这会创建新元组不影响 a判断要不要深拷贝的核心问题里面的东西能不能被原地修改能的话就需要深拷贝。本章小结列表[]是可变的有序容器元组()是不可变的列表常用操作append、insert、extend、remove、pop、sort复制列表要用.copy()或[:]直接是引用浅拷贝只复制外层嵌套的可变对象还是共享的深拷贝copy.deepcopy递归复制所有层元组不可变但可以做字典 key、解包赋值、性能更好单元素元组要加逗号(42,)面试题Q1列表和元组的区别是什么什么时候用元组点击查看答案核心区别列表可变元组不可变。用元组的场景数据不应该被修改时如坐标、配置项需要做字典的 key 时列表不行函数返回多个值时实际上就是返回元组需要更好的性能和更少的内存占用时元组因为不可变在内存中占的空间更小创建速度更快也可以被哈希hash。Q2a [1, 2, 3]; b a; b.append(4)a 的值是什么怎么避免这个问题点击查看答案a的值是[1, 2, 3, 4]。因为b a只是让b和a指向同一个列表对象不是创建副本。避免的方法创建浅拷贝b a.copy()b a[:]b list(a)如果列表里嵌套了可变对象如列表里有列表需要用copy.deepcopy(a)做深拷贝。Q3sort()和sorted()的区别点击查看答案sort()是列表的方法原地排序修改列表本身返回Nonesorted()是内置函数不修改原对象返回新的排序列表sorted()可以对任何可迭代对象排序列表、元组、字符串等sort()只能用于列表。两者都支持reverseTrue降序和key函数自定义排序规则参数。Q4append和extend有什么区别点击查看答案append(x)把x当作一个元素添加到列表末尾extend(iterable)把可迭代对象中的每个元素分别添加到列表末尾a[1,2]a.append([3,4])# [1, 2, [3, 4]]a.extend([3,4])# [1, 2, 3, 4]等价写法a.extend([3, 4])跟a [3, 4]效果一样。上一章 | 下一章字典与集合 →