第011课:常⽤数据结构之列表

在开始本节课的内容之前,我们先给⼤家⼀个编程任务,将⼀颗⾊⼦掷 6000 次,统计每个点数出现的次数。这个任务对⼤家来说应该是⾮常简单的,我们可以⽤1 6 均匀分布的随机数来模拟掷⾊⼦,然后⽤6个变量分别记录每个点数出现的次数,相信⼤家都能写出下⾯的代码。
import random
f1 = 0
f2 = 0
f3 = 0
f4 = 0
f5 = 0
f6 = 0
for _ in range(6000):
 face = random.randint(1, 6)
 if face == 1:
 f1 += 1
 elif face == 2:
 f2 += 1
 elif face == 3:
 f3 += 1
 elif face == 4:
 f4 += 1
 elif face == 5:
 f5 += 1
 else:
 f6 += 1
print(f'1点出现了{f1}次')
print(f'2点出现了{f2}次')
print(f'3点出现了{f3}次')
print(f'4点出现了{f4}次')
print(f'5点出现了{f5}次')
print(f'6点出现了{f6}次')
看看上⾯的代码,相信⼤家⼀定觉得它⾮常的 笨重 丑陋 ,更可怕的是,如果要统计掷 2 颗或者更多的⾊⼦统计每个点数出现的次数,那就需要定义更多的变量,写更多的分⽀结构。讲到这⾥,相信⼤家⼀定想问:有没有办法⽤⼀个变量来保存多个数据,有没有办法⽤统⼀的代码对多个数据进⾏操作?答案是肯定的,在Python 中我们可以通过容器类型的变量来保存和操作多个数据,我们⾸先为⼤家介绍列表(list )这种新的数据类型。
定义和使⽤列表
Python 中, 列表是由⼀系元素按特定顺序构成的数据序列 ,这样就意味着定义⼀个列表类型的变量, 可以保存多个数据 ,⽽且 允许有重复的数据 。跟上⼀课我们讲到的字符串类型⼀样,列表也是⼀种结构化的、⾮标量类型,操作⼀个列表类型的变量,除了可以使⽤运算符还可以使⽤它的⽅法。在Python中,可以使⽤ [] 字⾯量语法来定义列表,列表中的多个元素⽤逗号进⾏分隔,代码如下所示。
items1 = [35, 12, 99, 68, 55, 87]
items2 = ['Python', 'Java', 'Go', 'Kotlin']
除此以外,还可以通过 Python 内置的 list 函数将其他序列变成列表。准确的说, list 并不是⼀个函
数,⽽是创建列表对象的构造器(后⾯会讲到对象和构造器这两个概念)。
items1 = list(range(1, 10))
print(items1) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
items2 = list('hello')
print(items2) # ['h', 'e', 'l', 'l', 'o']
需要说明的是,列表是⼀种可变数据类型,也就是说列表可以添加元素、删除元素、更新元素,这⼀点跟我们上⼀课讲到的字符串有着鲜明的差别。字符串是⼀种不可变数据类型,也就是说对字符串做拼接、重复、转换⼤⼩写、修剪空格等操作的时候会产⽣新的字符串,原来的字符串并没有发⽣任何改变。
列表的运算符
和字符串类型⼀样,列表也⽀持拼接、重复、成员运算、索引和切⽚以及⽐较运算,对此我们不再进⾏赘述,请⼤家参考下⾯的代码。
items1 = [35, 12, 99, 68, 55, 87]
items2 = [45, 8, 29]
# 列表的拼接
items3 = items1 + items2
print(items3) # [35, 12, 99, 68, 55, 87, 45, 8, 29]
# 列表的重复
items4 = ['hello'] * 3
print(items4) # ['hello', 'hello', 'hello']
# 列表的成员运算
print(100 in items3) # False
print('hello' in items4) # True
# 获取列表的⻓度(元素个数)
size = len(items3)
print(size) # 9
# 列表的索引
print(items3[0], items3[-size]) # 35 35
items3[-1] = 100
print(items3[size - 1], items3[-1]) # 100 100
# 列表的切⽚
print(items3[:5]) # [35, 12, 99, 68, 55]
print(items3[4:]) # [55, 87, 45, 8, 100]
print(items3[-5:-7:-1]) # [55, 68]
print(items3[::-2]) # [100, 45, 55, 99, 35]
# 列表的⽐较运算
items5 = [1, 2, 3, 4]
items6 = list(range(1, 5))
# 两个列表⽐较相等性⽐的是对应索引位置上的元素是否相等
print(items5 == items6) # True
items7 = [3, 2, 1]
# 两个列表⽐较⼤⼩⽐的是对应索引位置上的元素的⼤⼩
print(items5 <= items7) # True
值得⼀提的是,由于列表是可变类型,所以通过索引操作既可以获取列表中的元素,也可以更新列表中的元素。对列表做索引操作⼀样要注意索引越界的问题,对于有 N 个元素的列表,正向索引的范围是 0到 N-1 ,负向索引的范围是 -1 -N ,如果超出这个范围,将引发 IndexError 异常,错误信息为: list index out of range
列表元素的遍历
如果想逐个取出列表中的元素,可以使⽤ for 循环的,有以下两种做法。
⽅法⼀:
items = ['Python', 'Java', 'Go', 'Kotlin']
for index in range(len(items)):
 print(items[index])
⽅法⼆:
items = ['Python', 'Java', 'Go', 'Kotlin']
for item in items:
 print(item)
讲到这⾥,我们可以⽤列表的知识来重构上⾯ 掷⾊⼦统计每个点数出现次数 的代码。
import random
counters = [0] * 6
for _ in range(6000):
 face = random.randint(1, 6)
 counters[face - 1] += 1
for face in range(1, 7):
 print(f'{face}点出现了{counters[face - 1]}次')
上⾯的代码中,我们⽤ counters 列表中的六个元素分别表示 1 6 的点数出现的次数,最开始的时候六个元素的值都是 0 。接下来⽤随机数模拟掷⾊⼦,如果摇出 1 counters[0] 的值加 1 ,如果摇出 2 点counters[1] 的值加 1 ,以此类推。⼤家感受⼀下,这段代码是不是⽐之前的代码要简单优雅很多。
列表的⽅法
和字符串⼀样,列表类型的⽅法也很多,下⾯为⼤家讲解⽐较重要的⽅法。
添加和删除元素
items = ['Python', 'Java', 'Go', 'Kotlin']
# 使⽤append⽅法在列表尾部添加元素
items.append('Swift')
print(items) # ['Python', 'Java', 'Go', 'Kotlin', 'Swift']
# 使⽤insert⽅法在列表指定索引位置插⼊元素
items.insert(2, 'SQL')
print(items) # ['Python', 'Java', 'SQL', 'Go', 'Kotlin', 'Swift']
# 删除指定的元素
items.remove('Java')
print(items) # ['Python', 'SQL', 'Go', 'Kotlin', 'Swift']
# 删除指定索引位置的元素
items.pop(0)
items.pop(len(items) - 1)
print(items) # ['SQL', 'Go', 'Kotlin']
# 清空列表中的元素
items.clear()
print(items) # []
需要提醒⼤家,在使⽤ remove ⽅法删除元素时,如果要删除的元素并不在列表中,会引发 ValueError异常,错误消息是: list.remove(x): x not in list 。在使⽤ pop ⽅法删除元素时,如果索引的值超出了范围,会引发 IndexError 异常,错误消息是: pop index out of range 。从列表中删除元素其实还有⼀种⽅式,就是使⽤Python 中的 del 关键字后⾯跟要删除的元素,这种做法跟使⽤ pop ⽅法指定索引删除元素没有实质性的区别,但后者会返回删除的元素,前者在性能上略优( del 对应字节码指令是 DELETE_SUBSCR ,⽽ pop 对应的字节码指令是 CALL_METHOD 和 POP_TOP )。
items = ['Python', 'Java', 'Go', 'Kotlin']
del items[1]
print(items) # ['Python', 'Go', 'Kotlin']
元素位置和次数
列表类型的 index ⽅法可以查找某个元素在列表中的索引位置;因为列表中允许有重复的元素,所以列表类型提供了 count ⽅法来统计⼀个元素在列表中出现的次数。请看下⾯的代码
items = ['Python', 'Java', 'Java', 'Go', 'Kotlin', 'Python']
# 查找元素的索引位置
print(items.index('Python')) # 0
print(items.index('Python', 2)) # 5
# 注意:虽然列表中有'Java',但是从索引为3这个位置开始后⾯是没有'Java'的
print(items.index('Java', 3)) # ValueError: 'Java' is not in list
再来看看下⾯这段代码。
items = ['Python', 'Java', 'Java', 'Go', 'Kotlin', 'Python']
# 查找元素出现的次数
print(items.count('Python')) # 2
print(items.count('Go')) # 1
print(items.count('Swfit')) # 0
元素排序和反转
列表的 sort 操作可以实现列表元素的排序,⽽ reverse 操作可以实现元素的反转,代码如下所示。
items = ['Python', 'Java', 'Go', 'Kotlin', 'Python']
# 排序
items.sort()
print(items) # ['Go', 'Java', 'Kotlin', 'Python', 'Python']
# 反转
items.reverse()
print(items) # ['Python', 'Python', 'Kotlin', 'Java', 'Go']
列表的⽣成式
Python 中,列表还可以通过⼀种特殊的字⾯量语法来创建,这种语法叫做⽣成式。我们给出两段代码,⼤家可以做⼀个对⽐,看看哪⼀种⽅式更加简单优雅。
通过 for 循环为空列表添加元素。
# 创建⼀个由1到9的数字构成的列表
items1 = []
for x in range(1, 10):
 items1.append(x)
print(items1)
# 创建⼀个由'hello world'中除空格和元⾳字⺟外的字符构成的列表
items2 = []
for x in 'hello world':
 if x not in ' aeiou':
 items2.append(x)
print(items2)
# 创建⼀个由个两个字符串中字符的笛卡尔积构成的列表
items3 = []
for x in 'ABC':
 for y in '12':
 items3.append(x + y)
print(items3)
通过⽣成式创建列表。
# 创建⼀个由1到9的数字构成的列表
items1 = [x for x in range(1, 10)]
print(items1) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 创建⼀个由'hello world'中除空格和元⾳字⺟外的字符构成的列表
items2 = [x for x in 'hello world' if x not in ' aeiou']
print(items2) # ['h', 'l', 'l', 'w', 'r', 'l', 'd']
# 创建⼀个由个两个字符串中字符的笛卡尔积构成的列表
items3 = [x + y for x in 'ABC' for y in '12']
print(items3) # ['A1', 'A2', 'B1', 'B2', 'C1', 'C2']
下⾯这种⽅式不仅代码简单优雅,⽽且性能也优于上⾯使⽤ for 循环和 append ⽅法向空列表中追加元素的⽅式。可以简单跟⼤家交待下为什么⽣成式拥有更好的性能,那是因为Python 解释器的字节码指令中有专⻔针对⽣成式的指令( LIST_APPEND 指令);⽽ for 循环是通过⽅法调⽤(( LOAD_METHOD 和CALL_METHOD 指令)的⽅式为列表添加元素,⽅法调⽤本身就是⼀个相对耗时的操作。对这⼀点不理解也没有关系,记住“ 强烈建议⽤⽣成式语法来创建列表 这个结论就可以了。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值