List(列表)知识:1年级1班的班级生活
1.1 列表是个啥:List(列表)的基础知识:
今天我们来讲Python六大数据类型的第一种:List(列表)。本文将引入一个1年级1班的班级故事,本故事将贯穿教程始末,力求让每一个小知识点第一次出现在你面前时,都是灵活而可爱的。通过本文的学习后,你就能掌握关于List(列表)的所有相关知识了。那,跟着我一起看下好不啦?
A.List是啥?
a. List属于序列:序列共有 List 、 Turple 、 字符串 、 Unicode字符串 、 buffer对象 和 xrange对象 六种,所有序列都存在内部的索引(将在后文提及)。
b. List属于标准数据类型:Python标准数据类型共有 Number(数字) 、 String(字符串) 、 List(列表) 、 Turple(元组) 、 Set(集合) 、 Dictionary(字典) 六类。
B.List有啥特征?
我们先来看几个List(序列)长啥样:
>>> a = ['Think','123456','[1,2,3]']
观察可以看到:
a. 序列的基本结构为: 变量 = [ ] ,即 变量 、 = 和 [] 。
b. 序列内的数据类型:不要求一致,可以有多种并存,甚至一个List里面还可以包含其他List。被包含的"子List"此时作为“母List”的元素。
c. 序列内的元素:序列内的内容为元素,元素数目可以为0,即空集 a = [] ;元素之间用 , 分隔开;如果元素为字符串,应用 ’ 或者 " 括起来。
C. 序列可以进行的基本操作:最基础的基本操作一共有五种: 索引 、 切片 、 加 、 乘 、 检查 ,将在后文中进行详细解释。
1.2 可以对列表做啥:List(列表)的操作
a. 创建:1年级1班开学了!
我们假设1年级1班开学了,一共有李布尔、王多鱼、苏大强三名小朋友入学。我们简称1年级1班的班名为 one ,现在我们要打印出这个班级的同学名单:
>>> one = ['李布尔','王多鱼','苏大强']
>>> one
['李布尔', '王多鱼', '苏大强']
用中括号[ ]创建一个列表赋给 one 。当然班级也可以没人,比如隔壁1年级2班没人,list可以是空的:
>>> two = []
>>> two
[]
b. 计数 :班里有几个人?
这时候老师想知道1年级1班有几个人,三个人很容易数出来,可如果想知道全校的人数呢?需要调用 len(班级名) 这个语句来计数列表内的元素长度(数目):
>>> one = ['李布尔','王多鱼','苏大强']
>>> len(one)
3
注意:空list也可以用len,空list长度为0。比如我们看看惨淡的1年级2班:
>>> two = []
>>> len(two)
0
c. 访问元素:班里都是按学号点名的!
在学校里,每名同学都有自己的学号,这就是列表中的元素索引。
学号的制定规律:
1.在一个有人的班级中,每名同学都有两个学号(即索引),这个学号是以他们在整个班级中的相对位置确定的。
2.正数第一位学号为 0 ,正数第二位学号为 1 …倒数第一位学号为 -1 ,倒数第二位学号为 -2 。即:正数索引为n-1,倒数索引为-n。
比如以1年级1班为例:
>>> one = ['李布尔','王多鱼','苏大强']
王多鱼是正数第一位同学、倒数第三位同学。所以他的学号就有 0 和 -3 两个。
当老师想点名的时候,要用 班级名[学号] 的方式点学号。例如点二号同学:
>>> one = ['李布尔','王多鱼','苏大强']
>>> one[2]
'苏大强'
如果老师想知道某个同学的姓氏怎么办?对于列表内的元素我们还可以继续切割:
>>> one = ['李布尔','王多鱼','苏大强']
# 查看 1年级1班 学号为 0 的同学姓名:
>>> name = one[0]
# 取该姓名索引为0的字符:
>>> name [0]
'李'
如果老师想叫多位同学,比如想叫从1号开始后面的所有同学呢?
>>> one = ['李布尔','王多鱼','苏大强']
>>> one[1:]
['王多雨', '苏大强']
我们可以看到,就成功把1号和2号同学叫出来了。这里用的是切片的方式,即 班级名[从第几号学生开始:截止到第几号学生之前] ,从第n号开始的全部可以用 班级名 [n:] ,截止到n号之前的全部可以用 班级名[:n] 。
d. 追加元素 :1年级1班的新生
班里转来了一名新生,叫史晓明。我们用 班级名.append(新同学名字) 的方式将他添到班级名单里,按照先来后到的规则,他自然排在最后:
第一种情况:默认添加新元素到列表末尾
>>> one = ['李布尔','王多鱼','苏大强']
>>> one.append('史晓明')
>>> one
['李布尔', '王多鱼', '苏大强', '史晓明']
这时候 ‘王多鱼’ 小朋友家财大气粗的派来一名随读保镖,强烈要求学号和王多鱼挨着。那能不能安排到指定位置呢?当然可以,我们可以使用 班级名.insert(保镖学号,‘保镖名字’) 的方式将其添加到指定位置:
第二种情况:指定位置插入元素
>>> one = ['李布尔', '王多鱼', '苏大强', '史晓明']
>>> one.insert(2,'钱大彪')
>>> one
['李布尔', '王多鱼', '钱大彪', '苏大强', '史晓明']
这时候需要注意,因为 ‘钱大彪’ 的加入,导致列表内新元素后面元素的相对位置都发生了一次变化。例如 ‘苏大强’ 过去学号是 2 和 -2 ,现在学号则变成了 3 和 -2 。
e.删除:报告老师,我想退学!
因为刚才 ‘钱大彪’ 的加入,导致 ‘苏大强’ 和 ‘史晓明’ 的学号都滞后一名。在调和失败后, ‘史晓明’ 同学愤而退学,老师只好将其从班级名单中删掉。在python中,将元素从列表中删除一共有三种方式:
第一种删除方式: remove 语句
班主任拿出了班级名单,找到了 ‘史晓明’ 的名字,然后用 班级名.remove(史晓明的姓名) 的方式将 ‘史晓明’ 在班级名单中删除了:
>>> one = ['李布尔', '王多鱼', '钱大彪', '苏大强', '史晓明']
>>> one.remove('史晓明')
>>> one
['李布尔', '王多鱼', '钱大彪', '苏大强']
第二种删除方式: del 语句
第一种用 remove 语句删除的情况,前提是老师记得 ‘史晓明’ 的名字。但万一老师不知道这个学生的名字,或者有熊孩子没事改名字记不住咋办?没事,我们还可以用他的学号删除(索引删除),方法为: del 班级名[史晓明学号] 。
>>> one = ['李布尔', '王多鱼', '钱大彪', '苏大强', '史晓明']
>>> del one[4]
>>> one
['李布尔', '王多鱼', '钱大彪', '苏大强']
第三种删除方式: pop 语句
我们用学号删除的时候容易出问题,万一删错了咋办?我咋知道哪个索引到底还是不是 ‘史晓明’ 的学号?。这时候我们可以用 pop 语句,同样也是用索引删除,但是pop删除元素后会给你显示删除学号对应的名字。具体方法为: 班级名.pop(史晓明的学号)
>>> one = ['李布尔', '王多鱼', '钱大彪', '苏大强', '史晓明']
>>> one.pop(4)
'史晓明'
>>> one
['李布尔', '王多鱼', '钱大彪', '苏大强']
我们可以看到执行了 班级名.pop(史晓明学号) 后,先给我们返回了一个 ‘史晓明’ 的值,告诉我们刚才删的是啥。我们在执行一下 one ,就会看到班级名单里已经删除了。
注意:当我们需要删除列表中的最后一个元素时,可以不写元素索引。像在刚才的例子中,用 班级名.pop() 即可删除位于班级名单最后的 ‘史晓明’ 同学:
>>> one = ['李布尔', '王多鱼', '钱大彪', '苏大强', '史晓明']
>>> one.pop()
'史晓明'
>>> one
['李布尔', '王多鱼', '钱大彪', '苏大强']
讲完 班级名.remove() 、 del 班级名[] 和 班级名.pop() 后,不得不将另一种删除元素的语句。这种语句与上述三种的不同在于,它会删除元素内所有元素:
第四种删除方式: list.clear() 语句
这种方式为清空列表内所有元素,将该列表变为空list:
>>> a = ('1','2','3')
>>> a.clear()
>>> a
[]
这时候如果我们想再确认一遍 ‘史晓明’ 同学到底还在不在班级名单里,可以使用 in 语句,方法为 要检查的同学名字 in 班级名 :
>>> one = ['李布尔','王多鱼','钱大彪','苏大强']
>>> '史晓明' in one
False
返回的结果是 False ,就说明史晓明已经不在班级名单中了。同样我们也可以用 要检查的元素 not in 序列 来判断元素是否不再序列中:
>>> one = ['李布尔','王多鱼','钱大彪','苏大强']
>>> '史晓明' not in one
True
这里要注意的是在 要检查的元素 not in 序列 和 要检查的元素 in 序列 中, in 后面的既可以是 变量 也可以是 序列本身 ,例如:
# 在变量中检索
>>> one = ['李布尔','王多鱼','钱大彪','苏大强']
>>> '史晓明' in one
False
# 在序列中检索
>>> one = ['李布尔','王多鱼','钱大彪','苏大强']
>>> '史晓明' in ['李布尔','王多鱼','钱大彪','苏大强']
False
确定将 ‘史晓明’ 从班级中删除后,班主任想点一遍名。 for in 循环语句终于出场了,用法为 for 标识符 in 序列 。执行语句后,Python会将 列表 中每一个 元素赋值给标识符,赋值结束后继续循环将第二个元素再重新赋值给标识符,直到将列表中所有数据赋过一遍结束。
>>> one = ['李布尔','王多鱼','钱大彪','苏大强']
>>> for i in one:
... print(i)
...
李布尔
王多鱼
钱大彪
苏大强
在上述代码中,即将序列 one 中的每一个元素依次赋给标识符 i ,即可理解为依次执行: i1 = ‘李布尔’、 i2 = ‘王多鱼’、 i3 = ‘钱大彪’、 i4 = ‘苏大强’,然后 print(i) 即依次打印之前的每一次 i值 ,即依次执行 print(i1)、 print(i2)、 print(i3)和 print(i4)。
**f. 更新与替换:王多鱼改名了
换名的情况还是发生了:有一天 ‘王多鱼’ 的家长联系老师,请老师更新一下班级名单,因为王多鱼刚刚改名了。老师问为啥?他说觉得 ‘王多鱼’ 这个名字太世俗了,要取一个有文化的名字: ‘王有才’ 。
老师虽是无奈,也只好作罢。用 班级名[王多鱼的学号] = 王多鱼的新名字 的方式将班级名单中,王多鱼的姓名记录更新了,从此以后王多鱼叫王有才:
>>> one = ['李布尔', '王多鱼', '钱大彪', '苏大强']
>>> one[1] = '王有才'
>>> one
['李布尔', '王有才', '钱大彪', '苏大强']
**g.合并班级
后来教务处做出一个决定,将1年级3班并入1年级1班。忙碌的班主任又要开始重新合并班级名单,我们先来看一下1年级3班的新同学们:
>>> three = ['杨九年','周媛','钱大彪','郑仕']
(好像有个哥们重名了??我们一会再说)
班主任这时调用了 extend 语句合并两个班的学生,方法为 1班名字.extend(3班名字)
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强']
>>> three = ['杨九年','周媛','钱大彪','郑仕']
>>> one.extend(three)
>>> one
['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
还是按照先来后到的道理:extend里新加入列表内的所有元素,都默认添加到了源列表的末尾。
初次之外还可以用 列表直接相加 的方式,例如:
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强']
>>> three = ['杨九年','周媛','钱大彪','郑仕']
>>> one = one + three
>>> one
['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
说到列表之间相互相加,我们再顺便谈谈列表的乘法。一个列表可以直接乘以一个数字,我们可以看看会得到什么样的结果:
>>> a = ['think','pad']
>>> a * 3
['think', 'pad', 'think', 'pad', 'think', 'pad']
我们发现:将列表乘以一个整数后,Python会将列表内所有元素按顺序重复输出相应的次数。
另外我们发现,1年级1班和1年级3班都有1名叫 ‘钱大彪’ 的同学,但是都不受影响的就放到了同一个列表中。我们可得知:列表中的元素是可重复的。
如果有重复,我们咋知道重复了几次呢?这时候我们可以用 ‘count’ 语句,用法为 班级名.count(像检查重复数的对象) :
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> one.count('钱大彪')
2
我们就知道,班里现在有两个 ‘钱大彪’ 的人了,我们可以按照学号次序管他们叫 ‘大钱大彪’ 和 ‘小钱大彪’,我们想查 ‘大钱大彪’ 的学号时,可以用 班级名.index(搜索条件) 的方式检索第一个匹配搜索的同学的学号(序列):
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> one.index('钱大彪')
2
这时候我们就可以快速知道,‘大钱大彪’ 的学号是 2 了。
**h. 排序与嵌套:1年级1班要分组了
为了班级合并后的有序管理,教务处给了1年级1班的班主任一个这样的任务:
- 要选出一名班长。
- 班长以外的七名同学分成两组,每组一名队长三名队员。
- 以学籍号正序为准,前三位分别为:班长、两名队员。
- 将班长名单、队长名单、队员名单提交至教务处,收到回复。
第一步:选出学籍号排名第一的同学为班长
一开始我们说每个都学都有自己的学号(索引),但是这容易出现这样一个问题:
在1年级1班里,学号为 0 的是 ‘李布尔’ 小朋友,但 ‘杨九年’ 小朋友在3年级1班的时候学号也是 0 。为了避免这种重新排序,我们要了解 学籍号 (id)这个概念,每个小朋友都有自己独一无二id,我们可以用 id(查看id的对象) 这个语句来查看:
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> id('苏大强')
2078505060944
>>> id('周媛')
2078503793656
首先要按照学籍号选出班长来。班主任在这时候调用了 min 语句,使用方法为 min(班级名):
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> min(one)
'周媛'
这时,姓名首字母排名最小的 ‘周媛’ 同学成为了班长。
顺便一提:我们看到 min 语句自然而然就能想到 max 语句,使用方法为 max() 语句。
max 和 min 语句其实可以设定排序条件的,方法为 min(班级名,key=排序方式) ,比如我们想看看1年级1班中谁名字最短,就可以用 len 即长度来作为判断标准 min(班级名,key=len) :
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> min(one,key=len)
'周媛'
我们会发现在班里 ‘周媛’ 和 ‘郑仕’ 都是两个字,但是执行后却给了我们 ‘周媛’ 的名字,原因是: min 或者 max 语句默认将符合条件的第一个作为结果返回。
第二步:按学籍号排名先后分出队长和队员
要分出队长和队员,首先我们要知道1年级1班同学们学籍号由小到大的排序吧,班主任用了 sort 语句给班内人排序,使用方法为 班级名.sort() :
>>> one = ['李布尔', '王有才', '钱大彪', '苏大强', '杨九年', '周媛', '钱大彪', '郑仕']
>>> one.sort()
>>> one
['周媛', '李布尔', '杨九年', '王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']
现在就一目了然了: 次序第一位的 ‘周媛’ 为班长、次序第二、三位的 ‘李布尔’ 、 ‘杨九年’ 为队长, 其余五人各为两组队员。
注意:在 sort() 函数中会默认地按升序排列
说到用 sort() 语句可以将一个列表正向排列,就不得不提到可以将列表倒叙排列的 reverse 语句:
>>> one = ['周媛', '李布尔', '杨九年', '王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']
>>> one.reverse()
>>> one
['钱大彪', '钱大彪', '郑仕', '苏大强', '王有才', '杨九年', '李布尔', '周媛']
这时候老师可以创建班长、队长、队员的三个名单了:
>>> monitor = ['周媛']
>>> captain = ['李布尔','杨九年' ]
>>> member = [ '王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']
但是这样分三行显示好像不太方便,机智的班主任使用了将 monitor 、 captain 和 member 合并给了一个新的班级名单:
>>> monitor = '周媛'
>>> captain = ['李布尔','杨九年' ]
>>> member = [ '王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']
>>> one = [monitor, captain, member]
>>> one
['周媛', ['李布尔', '杨九年'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
班主任完成了1年级1班开学以来的第一个重要任务,心满意足的给教务处交了差。
通过上述我们可以观察得知:list(列表)内还可以嵌套其他列表,但其他“列表”被嵌套进来后就已经成为了元素,我们可以检验一下:
>>> one = ['周媛', ['李布尔', '杨九年'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
>>> len(one)
3
此时再用 len(班级名) 检验发现,one 下只有三个元素。
**i. 复制:教务主任想把这次的名单备个份
教务主任想把这次1年级1班提交的班级名单拷贝一份,留在他的备份 Beifen 里,开始纠结起来了:列表(list)的备份方式有 直接赋值 、 浅拷贝 和 深拷贝 三种,这种情况下用哪种好呢?都试试吧!
**第一种拷贝:直接赋值
一开始教务主任想偷偷懒,直接把1年级1班提交的 one 列表赋值给我的备份文件 Beifen 不就好了 :
>>> one = ['周媛', ['李布尔', '杨九年'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
>>> Beifei = one
>>> Beifei
['周媛', ['李布尔', '杨九年'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
完美!可是直到有一天,教导主任发现他办公室里的那份 Beifen 文件里,怎么 ‘杨九天’ 自己变成了 ‘杨浩天’ ?他去问1年级1班的班主任,班主任告诉他:
杨九天改名了,我就把我手里的班级名单改了,怎么,您那儿的列表也变了?
是的,也改变了:
>>> captain[1] = '杨浩天'
>>> one
[['周媛'], ['李布尔', '杨浩天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
>>> Beifei
[['周媛'], ['李布尔', '杨浩天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
教务主任想:这不靠谱啊,这样我办公室文件就可以让提交人随便改了!
于是教导主任只能开始尝试第二种:
**第二种拷贝:浅拷贝
浅拷贝的使用方式主要有 拷贝到的地方 = 源列表.copy() 、 拷贝到的地方 = 源列表[:] 、 拷贝到的地方 = list(源列表) 效果相同的三种,教导主任就以第一种为例试一下:
>>> Beifei = one.copy()
>>> Beifei
[['周媛'], ['李布尔', '杨九天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
好像看起来又成功了,但教导主任不死心。让班主任再改一下他手里的班级文件,看看自己这里会不会变:
>>> captain[1] = '杨浩天'
>>> one
[['周媛'], ['李布尔', '杨浩天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
>>> Beifei
[['周媛'], ['李布尔', '杨浩天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
还是会变! 教导主任又放弃了第二种拷贝方式,将希望放在了最后一种上:
**第三种拷贝:深拷贝
深拷贝和刚才两种都不一样,使用深拷贝前需要先用 import 语句将 copy 调用出来,方法为 import copy 。调用后,深拷贝的使用方法为 拷贝到的地方 = copy.deepcopy(源列表) :
>>> import copy
>>> Beifei = copy.deepcopy(one)
>>> Beifei
[['周媛'], ['李布尔', '杨九天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
失败了两次的教务主任这时候已经不信结果了,执意要班主任再在他那改一次名字:
>>> captain[1]= '杨浩天'
>>> one
[['周媛'], ['李布尔', '杨浩天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
>>> Beifei
[['周媛'], ['李布尔', '杨九天'], ['王有才', '苏大强', '郑仕', '钱大彪', '钱大彪']]
成功了!这时候我们就明白了:在深拷贝中,当改变原序列中二层以及二层以下的内容时,拷贝后的内容不会受到任何影响。而直接赋值和浅拷贝的方式会受影响。
啥叫二层?
#这叫只有一层:
first = []
#这叫二层,存在列表嵌套
second = [[],[]]
到这里,我们关于list(列表)的叙述就告一段落。