给普通人的Python——第三章

3. 编程的基石:数据结构

所谓数据结构,简单说就是一种容器,用来盛放数据,就如同杯子、盆子一样。编程的本质实际上就是对数据进行处理,当我们对数据处理之前,当然先得有容器去存放数据,我们存取数据都需要和数据结构打交道,因此数据结构就是编程的基石。

3.1 列表(list)

关于列表的概念,其实生活中随处可见,比如我们考试之后的排名表,音乐播放器中的播放列表……列表的特点就是一组有顺序的数据,在我们Python编程中,列表是一种特指的数据结构,也可以说是一种数据类型,与我们生活中理解的列表大致相同。

说完了理论概念,我们回到实际编程中,现在我们的课题是用Python来模拟实现音乐播放列表。
首先想像一下,一个音乐播放列表有哪些功能

  1. 往列表中添加喜欢的音乐
  2. 把不喜欢的音乐移除列表
  3. 按顺序从列表中取一首音乐播放
  4. 随机从列表中取音乐播放

理清楚了音乐列表的基本功能,那我们就开始模拟,我这里从网上找到一个经典老歌排行榜单,我就选取前五名作为数据

# 1.创建并初始化音乐列表
music = ["当年情","大海","李香兰","来生缘","真的爱你"]

print(music)
print(music[0])

运行结果:

['当年情', '大海', '李香兰', '来生缘', '真的爱你']
当年情

如上代码,我们先学一个新知识,第1行以#号开头,后面写了一句话,这在编程中叫注释,顾名思义,就是写个注解,担心时间太久以后自己看不懂代码了,因此注释是写给人看的,注释是不会被Python执行的,运行代码的时候,所有注释都会被忽略,简单说就是Python会假装看不见这一行,直接跳过。

在上面的代码中,我们创建了一个列表,并将这个列表赋值给变量music,我们可以看到,这个列表中有五个数据,这些数据都被双"括起来了,特别需要注意一点,列表中是使用英文下的逗号分隔,千万不能写成中文逗号,说明这五个数据是五个字符串,也就是五首歌名。运行这段代码,第一个print()函数输出了music这个列表中的内容,也就是装了哪些数据,第二个print()函数则输出了当年情,这是第一首歌的名字,第5行代码中,music[0]的意思是从music这个列表中取第一个元素,元素是指列表中存放的数据,music变量后面跟一对中括号[],括号中写的就是想要取的元素的序号,这里要注意,在编程中,序号大都是从0开始的,因此music[0]取的是第一首歌,依次类推,music[4]取的就是最后一首歌名。

好了,说得太多,我们来看一看图
给普通人的Python——第三章

当我们打开Excel表格时,其实就很好理解列表了,如上图,这样有顺序的一组数据就是列表,元素的序号就是索引,从0开始。

我们接着写代码

# 1.创建并初始化音乐列表
music = ["当年情","大海","李香兰","来生缘","真的爱你"]
print(music)

# 2.往列表后面添加一个元素
music.append("你的眼神")
print(music)

运行结果:

['当年情', '大海', '李香兰', '来生缘', '真的爱你']
['当年情', '大海', '李香兰', '来生缘', '真的爱你', '你的眼神']

我们之前理定的功能,第一个就是往列表中添加喜欢的音乐,如上代码,我们通过调用列表的append()函数,实现向列表最后添加元素。append是追加的意思,也就是往最后面添加。我们添加一首歌之后,再次调用print函数打印输出music列表中的所有元素

让我们写代码再添加两首歌,并依次从列表中取出歌名

music = ["当年情", "大海", "李香兰", "来生缘", "真的爱你"]
music.append("你的眼神")
music.append("都是夜归人")
music.append("雨中的恋人们")

print(music[0])
print(music[1])
print(music[2])
print(music[3])
print(music[4])
print(music[5])
print(music[6])
print(music[7])

运行结果:

当年情
大海
李香兰
来生缘
真的爱你
你的眼神
都是夜归人
雨中的恋人们

好了,接下来我们在说一说如何从歌曲列表中移除一首

music = ["当年情", "大海", "李香兰", "来生缘", "真的爱你", "你的眼神", "都是夜归人", "雨中的恋人们"]

print(music)

# 移除索引为1的元素
music.pop(1)
print(music)

# 移除索引为0的元素
music.pop(0)
print(music)

运行结果:

['当年情', '大海', '李香兰', '来生缘', '真的爱你', '你的眼神', '都是夜归人', '雨中的恋人们']
['当年情', '李香兰', '来生缘', '真的爱你', '你的眼神', '都是夜归人', '雨中的恋人们']
['李香兰', '来生缘', '真的爱你', '你的眼神', '都是夜归人', '雨中的恋人们']

这次我们的列表已经有8首歌了,我们调用列表的pop()函数来移除指定的歌曲,该函数传入的参数就是我们前面说的元素的序号,即索引。第一次我们移除索引为1的歌曲,也就是第二首大海,这里要注意,每次移除元素之后的列表都发生了变化,下一个元素会占用之前元素的序号。这就好像我们排队的时候,中间走了一个人,后面的人就会马上跟进,是不会空出这个位置的。

遍历列表
所谓的遍历,就是指按顺序,从列表中一个一个的取数据,之前我们已经手动遍历过了,我们从music[0]一直写到music[7],但这样显然是不靠谱的,假如列表中存了1000个元素呢?我们根本不可能手动去取,这时候我们就要借助之前讲过的for循环来做

music = ["当年情", "大海", "李香兰", "来生缘", "真的爱你", "你的眼神", "都是夜归人", "雨中的恋人们"]

for name in music:
    print(name)

输出结果:

当年情
大海
李香兰
来生缘
真的爱你
你的眼神
都是夜归人
雨中的恋人们

可以看到,这和我们之前手动去取是一样的效果,for循环上次简单解释过,这里的用法也大致相同,就是每次从music列表里取一个元素赋值给变量name,在for循环的语句块中,我们去执行操作,这里是打印输出name变量

给普通人的Python——第三章

可迭代对象这个概念暂时不用纠结,只要记住,列表就是一种可迭代对象就行。

好了,最后我们来实现一下随机选歌的功能,还记得上一章我们引入random模块生成随机整数的例子吗?

# 引入random模块
import random

music = ["当年情", "大海", "李香兰", "来生缘", "真的爱你", "你的眼神", "都是夜归人", "雨中的恋人们"]

# 调用random模块的choice函数,传入一个列表
name = random.choice(music)
print(name)

反复多次运行上面的代码,可以发现,每次都是随机从music列表取一首歌,这次我们仍然引入random模块,但是调用的是另外一个函数choice(),它的参数需要传入一个列表,然后随机从列表中取一个元素并返回,我们这里使用name变量保存返回值。

最后,我们来一个完整的使用列表(list)模拟歌曲播放列表的例子

import random

# 1.创建并初始化音乐列表
music = ["当年情", "大海", "李香兰", "来生缘", "真的爱你"]

# 2.往列表后面添加元素
music.append("你的眼神")
music.append("都是夜归人")
music.append("雨中的恋人们")

# 3.按顺序取出歌曲
for name in music:
    print("播放歌曲:", name)


# 4.随机取出歌曲
music_name = random.choice(music)
print("随机歌曲:", music_name)

补充
在上面的例子中,我们在创建的同时还初始化了列表,所谓初始化,就是在创建的同时还往列表中注入了数据,实际上我们在使用列表的时候,可以只创建一个空的列表而不填入数据。

data = []

print(data)

输出结果:

[]

另外,列表是可以装入多种类型的数据的,不仅仅是字符串,还可以是整数类型,可以是浮点类型(即小数),如下

data = ["张三", 20, 1.75]

print(data)

3.2 字典(dict)

字典也是Python中的一种数据结构,或者说是数据类型,正如其名,让我们回想一下新华字典有什么特点。总的来说,字典是一种配对关系,就是某个字或词对应一项解释,用数学的语言来形容这就是映射,一对一的关系。

其实这种映射关系在我们生活中也非常常见。比如说我们的身份证,上面的身份证号码和姓名就是一种映射关系,一个身份证号码对应一个姓名,姓名可能相同,但身份证号码绝不能相同,这一点也正好描述了Python字典(dict)的另一个特点,在Python的字典中,一条数据由两部分构成,分别称为键(key)和值(value),键的内容是不能相同的,也就是说不能重复,而值的内容可以相同。

让我们再来看一个例子

给普通人的Python——第三章

可以看到,邮政编码就是一种典型的映射关系,一个邮编对应一个市,是不会相同的。

说了这么多,赶紧写一下代码压压惊吧

# 创建一个空的字典
people = {}
print(people)

# 创建一个字典并初始化
info ="4200010":"张三","4200011":"李四"print(info)

运行结果:

{}
{'4200010': '张三', '4200011': '李四'}

可以看到,与前面学的列表不同,字典是用大括号括起来的,但每一项数据仍然使用逗号分隔,冒号的左右两边也就是我们上面说的键与值。这里再次强调,编程中写的一些符号都是英文下的,请将输入法切到英文状态再输入,别问为什么,因为编程都是老外发明的,你懂的。

给普通人的Python——第三章

如图,Python字典中存放的是一项数据,分别由键与值两部分构成,这一项数据,亦被称为字典中的一个元素。到这里我们就明白了,如果我们现在要做一个根据身份证号码查询姓名的小程序,那么就应该使用字典这种数据结构,而不是列表,同时,只能将身份证号码作为键,姓名作为值来存放,因为键是不允许重复的,但是值可以重复。

我们修改一下上面的代码,将两人的id都改为"4200010"

info = {"4200010": "张三", "4200010": "李四"}
print(info)

运行结果:

{'4200010': '李四'}

可以看到,键相同的两条数据无法同时存放到字典,并且后面的一条数据覆盖了前面的。

最后看一下字典的常规使用

# 创建一个空的字典
code = {}

# 向字典中添加数据
code["510000"] = "广州市"
code["516600"] = "汕尾市"
code["516500"] = "陆丰市"
code["529500"] = "阳江市"

print(code)

# 根据键查询值
print("邮编:516500 对应的市是", code["516500"])

# 删除一条数据
code.pop("529500")
print(code)

运行结果:

{'510000': '广州市', '516600': '汕尾市', '516500': '陆丰市', '529500': '阳江市'}
邮编:516500 对应的市是 陆丰市
{'510000': '广州市', '516600': '汕尾市', '516500': '陆丰市'}

向字典添加数据也是很简单的,通过字典变量[键]=值的形式添加。这里需要注意一点,通常可以使用整数类型和字符串类型的数据作为键,但是涉及到id之类的情况,比如身份证号,建议使用字符串作为id类型,因为身份证通常18位数字,如果用整数类型基本没法表示了,18位已经是一个天文数字了,但是字符串类型就没关系了,因为它表示的只是文本符号,不是能用于计算的数字,另外001之类的id同样无法用整数表示,因为十进制整数不能0开头,用字符串"001"表示就没问题了。

编写查词小程序
基础知识讲得太多未免无趣,那就开始写一个小例子吧,我们编写一个查单词的小程序

# 首先创建一个单词字典
word_dict = {
    "meet": "v.会面", "family": "n.家庭",
    "read": "v.阅读,读", "grandfather": "n.祖父,外祖父",
    "grandmother": "n. 祖母,外祖母", "father": "n.父亲",
    "mother": "n.母亲", "brother": "n.兄弟", "sister": "n.姐妹",
    "hi": "喂,你好", "same": "a.相同的",
    "flat": "n.套房,公寓", "all": "a.&pron.所有的",
    "live": "v.&n.住,生活"
}

while True:
    # 接收用户的输入,并保存到word变量中
    word = input("请输入单词:")
    print(word_dict[word])

就这么几行代码,我们就编写了一个类似电子词典的程序。运行起来就可以愉快的查单词了,虽然我们的词汇表很少。我们经过多次测试发现这个程序有一个问题,那就是查询一个不在单词表中的单词就会报错退出,让我们改进一下这个程序

word_dict = {
    "meet": "v.会面", "family": "n.家庭",
    "read": "v.阅读,读", "grandfather": "n.祖父,外祖父",
    "grandmother": "n. 祖母,外祖母", "father": "n.父亲",
    "mother": "n.母亲", "brother": "n.兄弟", "sister": "n.姐妹",
    "hi": "喂,你好", "same": "a.相同的",
    "flat": "n.套房,公寓", "all": "a.&pron.所有的",
    "live": "v.&n.住,生活"
}

while True:
    # 接收用户的输入,并保存到word变量中
    word = input("请输入单词:")

    result = word_dict.get(word, -1)

    if result == -1:
        print("对不起,查询不到:", word)
    else:
        print(result)

测试结果:

请输入单词:xxx
对不起,查询不到: xxx
请输入单词:read
v.阅读,读
请输入单词:

这里首先解释一下,为什么之前查询单词会报错退出,这是因为使用字典变量[键]这种方式从字典中取数据时,如果这个键不存在字典中就会报错,所以不建议使用这种方式从字典中取数据,我们改进之后的代码中,调用了字典的get()函数来取数据,好处就是即使查询的键不存在也不会报错。要注意,我们现在又新学了一个字典的函数get,这个函数可以传两个参数,第一个就是需要查询的键,第二个参数就是默认值。这里默认值是做什么的呢?所谓默认值,就是当我们要查询的键在这个字典中不存在时返回的值。

这里result = word_dict.get(word, -1)这句代码的意思就是说,从word_dict字典中查询键为word的那条数据的值,并保存到变量result中,如果查不到word这个键,那么变量result的值就等于-1。有了这个设定,后面的代码就很好理解了,result == -1那肯定就是查不到嘛,我们就输出一个提示,如果不等于-1,那肯定就是查到了。

补充
之前我们写的猜数字小游戏仍有一些瑕疵,但为了降低难度我们没有一次就完善它,这里再对它做一个改进

import random


random_number = random.randint(1, 100)

for i in range(8):
    n = input("请猜一个数字:")
    num = int(n)

    if num == random_number:
        print("恭喜你,猜对了呢!")
        break
    elif i == 7:
        print("您第8次仍未猜中,游戏结束")
    elif num > random_number:
        print("你猜的数字大了呢!")
    else:
        print("你猜的数字小了呢!")

看到上面的核心代码,我们在复合判断语句中又加了一个elif i == 7:的语句块,之前我们说过,要控制游戏的次数,这里我们指定的是8次,变量i是从0开始的,当i == 7:的时候实际上就已经是第8次了,那么就是说,如果走到elif i == 7:时,玩家仍然没有猜对数字,因此我们在这里打印输出一个提示。

请关注公众号:编程之路从0到1

给普通人的Python——第三章