Python系列教程
第1章 Python 语言概述
Python编程规范+最佳实践
python文件:.py,.ipynb, pyi, pyc, pyd, pyo都是什么文件?
第1章必背的内容
第2章 Python基础
字符串切片
Python数据类型大战:可变与不可变,谁主沉浮?
Python「布尔类型」:不只是True和False!
Python「枚举类型」:你真的了解枚举类型吗?
Python字符串格式化:哪种字符串格式化方法最快?
Python字符串编码:为什么你的网页显示乱码?
Python「内置变量」:不只是变量,更是编程的魔法!
Python变量的作用域,你真的了解吗?
Python中如何使用F-Strings格式化浮点数
第2章必备的内容
第3章 流程控制和异常处理
加速Python for循环
for循环
Python三元表达式:让代码简洁与效率提升成为可能
Python「While循环」:何时打破循环?
Python「异常处理」:程序出错了?不慌
这样可以减少IF语句的使用,从此告别if-else噩梦
Python循环加速的秘方,可提速上千倍!
如何在Python中优雅地使用断言?这篇文章给你答案!
Python异常处理:12个异常处理技巧,你掌握了几个?
Python中的and和or你真的会用吗,代码逻辑竟然可以如此简单!
Python的else子句7个妙用,原来还能这样用,整挺好!
快来看!Python写代码,没有pass怎么行?
第三章 必背的内容
第4章 高级数据结构
Python字典嵌套:编程界的俄罗斯套娃
Python「类型注解」:如何让你的Python代码够清晰?
列表越界了?来学学Python列表的花式操作!
Python字典的这些黑科技,你用过几个?
Python元组:为何你的代码需要这个'不变'的伙伴?
试试Python具名元组NamedTuple吧!用过的都说好
Python中的5种队列数据结构,你知道几个?
itertools模块让你的代码原地起飞!
第5章 正则表达式
正则表达式
本文档使用 MrDoc 发布
-
+
首页
第4章 高级数据结构
  在前面的章节中主要介绍了Python的基本数据类型,了解了各个基本类型的特点和用法,后续又学习了流程控制与异常处理等知识,为了能够接触更深层次的业务逻辑,本章节将学习高级数据结构。   本章会先介绍常用的`容器`类型,包括`列表`、`元组`、`字典`等,并给出相应的操作实例。其次,再介绍一个综合上机案例,考察对之前章节和本章所学知识的综合运用。最后,以上机任务的形式对所学知识进行总结与反思。 # 4.1 列表   日常生活中乘坐的地铁就有点像列表,里面可以坐各式各样的人,也可以放各类行李。而`列表(list)`作为最常用的`容器`类型之一,它就像地铁一样,可以盛放各种数据类型,也可以盛放更为复杂的数据结构,但更倾向于保存同一类型的数据,因为更容易操作。例如:列表中要存数字都存数字,要存字典都存字典。 ## 4.1.1 列表基础操作   列表的基本操作主要包括创建空列表、带有元素的列表、元素的增、删、改、读等操作,后面还有性能对比。本节内容在以后的高阶编程阶段将有很高的使用率,请熟练掌握。 ### 1、创建空列表   想要使用列表,必须先学会如何创建列表,所谓`造物之前必先造人`。接下来讲解2种创建列表的形式。   (1)使用列表自身的构造方法来创建列表,代码如下所示: ```python students = list() ```   (2)使用语法更为简洁的“字面量”写法,用[]来表示列表,代码如下所示: ```python students = [] ```   从上述的两种写法中可以发现,列表中没有存放任何数据,因此由上述两种写法创建的列表称之为空列表,且两种写法从实际结果来讲是等价的,推荐熟练掌握第二种写法,更为方便好记。   这里需要解释一下,为什么等号左侧的变量命名为`students`。从英语的语法角度来分析,`student`表示单数,理解为一个学生,`students`表示复数,理解为一堆学生。列表就像一辆空的地铁,虽然可以乘坐各类乘客,但是如果计划这辆地铁只乘坐学生,那么用`students`命名就很符合逻辑。相当于从这辆坐了一堆学生的地铁中挑出来的人肯定是一位独立的学生,也可以根据实际情况进行命名,只要符合语义即可。 ### 2、创建带有元素的列表   如果想往列表中添加一些数据,或者说想创建一辆坐了一些乘客的地铁,那么代码该怎么写?其实很简单,格式如下: ```python students = list(元素1, 元素2, 元素3…) ``` 或者 ```python students = [元素1, 元素2, 元素3…] ```   列表中的每个数据统一称作为`元素`,且元素和元素之间需要用`逗号`隔开,此处的元素可以是`Python`中的任意数据类型,举例如下:   【例4-1】创建一个列表,变量名为`students`,里面存放若干个学生姓名,包括张三、李四、王五等三个字符串并打印该列表。 ```python students = ['张三', '李四', '王五'] print(students) ```   运行结果 ```python ['张三', '李四', '王五'] ```   是不是很简单?各位可以仿照上面的写法多加练习。此处有一个细节,虽然在创建列表的时候里面放的字符串都是双引号,但是打印的结果却是单引号,想到了什么?`Python`自动对字符串做了转换,所以将来再使用字符串的时候尽量使用单引号,因为最终所有的字符串都会以单引号的形式来表示,何必让`Python`多转换一次呢? ### 3、向列表插入数据   当地铁到站的时候,有些乘客会上来,那么列表如何才能添加一些`乘客`呢?此处介绍2种写法。   (1)向列表追加元素,使用`append`方法。   【例4-2】向`students`列表中追加元素`赵六`。 ```python students = ['张三', '李四', '王五'] print('追加前', students) students.append('赵六') print('追加后', students) ```   运行结果 ```python 追加前 ['张三', '李四', '王五'] 追加后 ['张三', '李四', '王五', '赵六'] ```   发现赵六被添加到了列表最末的位置,此处的`append`方法翻译为追加,其特点就是将新的数据追加到列表末尾。   注意:此操作速度最快,为真正意义的追加元素,就像排队的时候新来的人直接向后接着排队,并不影响其他人,推荐各位使用。   (2)向列表中插入元素,使用`insert`方法。   【例4-3】向students列表中插入元素`孙七`和`周八`。 ```python students = ['张三', '李四', '王五', '赵六'] print('插入孙七前', students) students.insert(0, '孙七') print('插入孙七后', students) print('插入周八前', students) students.insert(3, '周八') print('插入周八后', students) ```   运行结果 ```python 插入孙七前 ['张三', '李四', '王五', '赵六'] 插入孙七后 ['孙七', '张三', '李四', '王五', '赵六'] 插入周八前 ['孙七', '张三', '李四', '王五', '赵六'] 插入周八后 ['孙七', '张三', '李四', '周八', '王五', '赵六'] ```   `insert`翻译过来就是插入的意思。通过观察发现孙七插入到了整个列表的最前面,周八插入到了李四后面。此处的0和3该怎么理解?这里的0和3其实就是指元素的编号,跟日常生活中碰到的编号不太一样,生活中的编号一般都从1开始,但是列表中的编号实际上是从0开始,这么想就说的通了,如图4-1所示:    `students.insert(0, '孙七')`的意思是将孙七插入到0号位置,原先0号及之后的数据向后顺移;`students.insert(3, '周八')`意思是将周八插入到3号位置,3号及之后的数据向后顺移。   `思考一`:如果执行代码`students.insert(100, '吴九')`,那么吴九会插入到列表的哪个位置?答案:最后1个位置。   `思考二`:如果执行代码`students.insert(-2, '郑十')`,那么郑十会插入到列表的哪个位置?答案:倒数第3个位置。   注意:当元素插入后,其位置之后的所有数据都需要进行移动,当数据量大的时候会影响代码效率,好比说长长的队伍中突然有人要插队,所有人不得不后移,那感觉怎一个气愤了得。 ### 4、读取列表数据   如果想读取列表中某个位置的元素该怎么办?可以通过下标操作符来实现。下标操作符写为`list[index]`,其中`list`为列表对象,`index`为具体的编号,注意:此处的编号`从左至右`依次为`从0开始且加1递增`,一般称该编号为`索引`。由于从0开始,故索引的范围应该是`0~元素总个数-1`。此处的总个数可以通过`Python`内置的`len()`函数来获得,故索引范围也可以表示为`0~len(list)-1`。   【例4-4】读取`students`列表中索引为2的数据。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] student = students[2] print(student) ```   运行结果 ```python 李四 ```   从索引范围来看,孙七为0,张三为1,李四为2,故输出李四符合逻辑要求。   思考:如果打算读取`students`列表中倒数第2个数据,代码该怎么写? ```python student = students[-2] ```   `索引从右往左`表示,则范围是`-1~-len(列表)`。也就是说,列表中的索引有2种状态,一种是`正索引`,表示从左往右数且从0开始;一种是`负索引`,表示从右往左数且从-1开始。 ### 5、修改列表数据   如果想修改列表中的某个位置的数据该怎么办?依然可以通过索引来实现。   【例4-5】修改students列表中的`周八`为`周小八`。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('修改前', students) students[3] = '周小八' print('修改后', students) ```   运行结果 ```python 修改前 ['孙七', '张三', '李四', '周八', '王五', '赵六'] 修改后 ['孙七', '张三', '李四', '周小八', '王五', '赵六'] ```   索引位置为3的周八被修改为了周小八,符合题目要求。 ### 6、删除列表数据   有时候需要根据情况删除列表中的某些数据,代码该怎么写?可以从英语单词的角度出发,先思考一下`删除`用英语怎么说?`Delete`、`Remove`等都有删除的意思,那么看一看列表如何删除数据。   (1)使用`remove`方法删除指定元素。   【例4-6】删除`students`列表中的“张三”。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('删除前', students) students.remove('张三') print('删除后', students) ```   运行结果 ```python 删除前 ['孙七', '张三', '李四', '周八', '王五', '赵六'] 删除后 ['孙七', '李四', '周八', '王五', '赵六'] ```   发现列表中的张三被删除。   `思考`:如果列表中有多个张三,如果还是通过remove删除,那么会删除第一个还是所有张三?   【例4-7】删除students列表中的多个“张三”。 ```python students = ['孙七', '张三', '李四', '张三', '周八', '王五', '赵六'] print('删除前', students) students.remove('张三') print('删除后', students) ```   运行结果 ```python 删除前 ['孙七', '张三', '李四', '张三', '周八', '王五', '赵六'] 删除后 ['孙七', '李四', '张三', '周八', '王五', '赵六'] ```   发现只删除了第一个张三。   那如果列表中放的不是字符串,而是一堆数字,并且删除其中的某一个,是否也符合上述结论呢?   【例4-8】删除`numbers`列表中的数字`1`。 ```python numbers = [1, 2, 3, 1, 1, 4, 5] print('删除前', numbers) numbers.remove(1) print('删除后', numbers) ```   运行结果 ```python 删除前 [1, 2, 3, 1, 1, 4, 5] 删除后 [2, 3, 1, 1, 4, 5] ```   发现也只删除了符合要求的第1个数据。   故可以得出结论:`remove`方法可以删除符合要求的第1个数据。   `思考`:若删除的元素并不在列表中会怎么样?当然会报错!   (2)使用`del`方法删除指定元素。   【例4-9】删除`students`列表中的`李四`。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('删除前', students) del students[2] print('删除后', students) ```   运行结果 ```python 删除前 ['孙七', '张三', '李四', '周八', '王五', '赵六'] 删除后 ['孙七', '张三', '周八', '王五', '赵六'] ```   发现索引为2的李四被删除。`del`属于`Python`内置的关键字,不能算做是列表的方法,需要注意。   `注意`:`remove`方法并不推荐使用,好比说有10个人在排队,如果中间第2个人有事离开了,那么他身后的人需要依次向前填补位置,影响效率。   (3)使用`pop`方法删除指定元素。   【例4-10】删除`students`列表中的`周八`。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('pop()删除前', students) students.pop() print('pop()删除后', students) print('pop(3)删除前', students) students.pop(3) print('pop(3)删除后', students) ```   运行结果 ```python pop()删除前 ['孙七', '张三', '李四', '周八', '王五', '赵六'] pop()删除后 ['孙七', '张三', '李四', '周八', '王五'] pop(3)删除前 ['孙七', '张三', '李四', '周八', '王五'] pop(3)删除后 ['孙七', '张三', '李四', '王五'] ```   通过观察发现,`pop()`如果不指定具体索引,则默认删除最后一个元素;如果指定索引,则删除指定索引位置的元素。   `思考`:如果指定的索引超过实际范围会怎么样?当然是报错!难道你可以丢弃你本来就不曾拥有的东西吗?   【例4-11】删除`students`列表中倒数第10个元素。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('pop(-10)删除前', students) students.pop(-10) print('pop(-10)删除后', students) ```   运行结果 ```python pop(-10)删除前 ['孙七', '张三', '李四', '周八', '王五', '赵六'] Traceback (most recent call last): students.pop(-10) IndexError: pop index out of range ```   果然报错,而且报了一个`IndexError`类型的错误,且错误原因是因为`pop index out of range`,也就是所谓的`索引越界`。以后在运行代码的过程中,如果碰到了类似的错误,就想一想是不是索引在使用的时候超过了实际范围。   本节讲解的三种方式都能删除元素,那么哪种效率更快呢?可以通过接下来的脚本进行测试。   【例4-12】对比`pop`、`del`、`remove`三种方式删除元素的性能。 ```python import random import time from copy import deepcopy deal_datas = [random.randint(1, 1000) for x in range(100000)] deal_datas1 = deepcopy(deal_datas) deal_datas2 = deepcopy(deal_datas) deal_datas3 = deepcopy(deal_datas) start = time.time() for x in range(100000): deal_datas1.pop() end = time.time() total = end-start print("pop()删除10000个数据耗时", total) start = time.time() for x in deal_datas2: del deal_datas2[x] end = time.time() total = end-start print("del删除10000个数据耗时", total) start = time.time() for x in deal_datas3: deal_datas3.remove(x) end = time.time() total = end-start print("remove删除10000个数据耗时", total) ```   运行结果 ```python pop()删除100000个数据耗时 0.009974002838134766 del删除100000个数据耗时 0.735072135925293 remove删除100000个数据耗时 2.460418939590454 ```   `结论`:列表中存放了10万个随机数字,分别通过3种方式删除所有元素,从代码性能的角度会发现,`pop>del>remove`,也就是说`pop`最快,`remove`最慢,尤其数据量越大越明显,感兴趣的读者可以编写脚本分别测试一下以万、十万、百万为单位的代码耗时,看看性能有什么样的变化。 ## 4.1.2 列表内置的常用方法   上一小节主要介绍了列表的基础操作,简单但重要。本节将介绍列表常用的方法,种类略多,各位请做好笔记。 ### 1、count方法   `count`翻译为`计数或总数`,在这里就是统计指定元素的出现次数。   【例4-13】统计`students`列表中`张三`出现的次数。 ```python students = ['孙七', '张三', '李四', '周八', '张三', '王五', '赵六'] print('张三出现了', students.count('张三'), '次') ```   运行结果 ```python 张三出现了 2 次 ``` ### 2、extend方法   `extend`翻译为扩展或延伸,在这里指追加数据的一种方式。   【例4-14】向`students`列表添加`new_students`列表中元素。 ```python students = ['孙七', '张三', '李四', '周八',] new_students = ['王五', '赵六'] students.extend(new_students) print('扩展后的列表', students) ```   运行结果 ```python 扩展后的列表 ['孙七', '张三', '李四', '周八', '王五', '赵六'] ```   `注意`:此方法会直接将所有元素扩展至列表末尾,并不会创建新的列表。 ### 3、index方法   `index`在前端领域通常表示为首页的意思,在列表中被翻译为索引。   【例4-15】获取`张三`在`students`列表中的索引。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('张三的索引为', students.index('张三')) ```   运行结果 ```python 张三的索引为 1 ``` ### 4、reverse方法   `reverse`翻译为颠倒、使反转,在列表中理解为反向,就是所有元素前后颠倒。   【例4-16】将`students`列表中的所有元素反向。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] students.reverse() print('反向后的列表', students) ```   运行结果 ```python 反向后的列表 ['赵六', '王五', '周八', '李四', '张三', '孙七'] ```   所谓反向就是将数据按照顺序前后颠倒而已。 ### 5、sort方法   `sort`直接翻译是分类、种类、排序的意思,在代码中一般都理解为排序。   【例4-17】对`students`列表中的数据进行排序。 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] students.sort() print('排序后的列表(默认升序) ', students) students.sort(reverse=True) print('排序后的列表(改为降序) ', students) ```   运行结果 ```python 排序后的列表(默认升序) ['周八', '孙七', '张三', '李四', '王五', '赵六'] 排序后的列表(改为降序) ['赵六', '王五', '李四', '张三', '孙七', '周八'] ```   对于字符串来讲,排序后的结果让人一头雾水,可以换成数字试试。   【例4-18】对`numbers`列表中的数字进行排序。 ```python numbers = [1, 3, 2, 1, 5, 4, 9] numbers.sort() print('排序后的列表(默认升序) ', numbers) numbers.sort(reverse=True) print('排序后的列表(改为降序) ', numbers) ```   运行结果 ```python 排序后的列表(默认升序) [1, 1, 2, 3, 4, 5, 9] 排序后的列表(改为降序) [9, 5, 4, 3, 2, 1, 1] ```   `注意`:列表内容原地修改,并不会生成新的列表。 ### 6、clear方法   `clear`理解为清空。   【例4-19】清空`numbers`列表中的所有元素。 ```python numbers = [1, 3, 2, 1, 5, 4, 9] numbers.clear() print('清空后的列表', numbers) ```   运行结果 ```python 清空后的列表 [] ``` ### 7、copy方法   翻译为拷贝。只不过拷贝又分为`浅拷贝`和`深拷贝`,后面的章节再进行详细介绍。   【例4-20】复制`old_numbers`列表为新列表。 ```python old_numbers = ['孙七', '张三', '李四', '周八', '王五', '赵六'] new1_numbers = old_numbers new2_numbers = old_numbers.copy() new1_numbers.pop(2) new2_numbers.pop(3) print('old_numbers', old_numbers) print('new1_numbers', new1_numbers) print('new2_numbers', new2_numbers) ```   运行结果 ```python old_numbers ['孙七', '张三', '周八', '王五', '赵六'] new1_numbers ['孙七', '张三', '周八', '王五', '赵六'] new2_numbers ['孙七', '张三', '李四', '王五', '赵六'] ```   通过结果会发现,`old_numbers`和`new1_numbers`一模一样,可是明明删除的是`new1_numbers`,为什么`old_numbers`也会受影响?这就是赋值和拷贝的区别。拷贝有点像克隆,会创建一个一模一样的人,但是两者又没有联系,但赋值本质上两者就是一个人,只不过名字换了换而已。 ## 4.1.3 作用于列表的其它函数   上一小节讲解了列表自带的方法,其实`Python`还有一些好用的函数,也可以作用于列表。 ### 1、reversed函数   本质和`list.reverse`一样,都是用来反向的。   【例4-21】将`old_numbers`列表的所有元素反向。 ```python old_numbers = [1, 3, 2, 1, 5, 4, 9] new_numbers = reversed(old_numbers) print('反向前的列表', old_numbers) print('反向后的列表', new_numbers) print('反向后的列表', list(new_numbers)) ```   运行结果 ```python 反向前的列表 [1, 3, 2, 1, 5, 4, 9] 反向后的列表 <list_reverseiterator object at 0x000002153D74C438> 反向后的列表 [9, 4, 5, 1, 2, 3, 1] ```   运行结果中的`reverseiterator`翻译过来叫做反向迭代器,一个看似高级其实可以当做列表来使用的一个概念而已,通过`list(xxx)`强制转成列表来使用就好了。 ### 2、sorted函数   本质和`list.sort`一样,是用来对元素进行排序的。   【例4-22】对`old_numbers`列表中的元素进行排序。 ```python old_numbers = [1, 3, 2, 1, 5, 4, 9] new_numbers = sorted(old_numbers) print('排序前的列表', old_numbers) print('排序后的列表', new_numbers) ```   运行结果 ```python 排序前的列表 [1, 3, 2, 1, 5, 4, 9] 排序后的列表 [1, 1, 2, 3, 4, 5, 9] ``` ### 3、zip函数   `zip`后缀是一种压缩文件的后缀,顾名思义它就跟压缩有关。   【例4-23】对`students`、`ages`、s`exs`三个列表进行“压缩”。 ```python students = ['张三', '李四', '王五'] ages = [35, 40, 29, 30] sexs = ['男', '女', '保密'] print('压缩姓名和年龄', zip(students, ages)) print('压缩姓名和年龄', list(zip(students, ages))) print('压缩姓名和年龄和性别', zip(students, ages, sexs)) print('压缩姓名和年龄和性别', list(zip(students, ages, sexs))) ```   运行结果 ```python 压缩姓名和年龄 <zip object at 0x00000150FC683B08> 压缩姓名和年龄 [('张三', 35), ('李四', 40), ('王五', 29)] 压缩姓名和年龄和性别 <zip object at 0x00000150FC683B08> 压缩姓名和年龄和性别 [('张三', 35, '男'), ('李四', 40, '女'), ('王五', 29, '保密')] ```   `注意`:如果压缩的列表长度不一致,则压缩后的列表与长度最短的列表长度一致。 ### 4、enumerate函数   `enumerate`简称`enum`,翻译为枚举,意为将列表中的元素加上编号。   【例4-24】对`students`列表进行`枚举`。 ```python students = ['张三', '李四', '王五'] print('枚举列表的所有元素', enumerate(students)) print('枚举列表的所有元素', list(enumerate(students))) ```   运行结果 ```python 枚举列表的所有元素 <enumerate object at 0x000001684884D3F0> 枚举列表的所有元素 [(0, '张三'), (1, '李四'), (2, '王五')] ```   从运行结果中会发现,每个列表的元素都被赋予了一个编号,和索引对应的值相同,在将来如果既需要索引,又需要元素内容,就可以通过该函数来实现,和`for`循环搭配使用效果更佳。 ### 5、max函数   `max`属于数学用语,指提取指定范围中的最大的一个数字,翻译为取最大值。   【例4-25】获取`numbers`列表中的最大值。 ```python numbers = [1, 3, 2, 1, 5, 4, 9] print('列表中的最大值', max(numbers)) ```   运行结果 ```python 列表中的最大值 9 ``` ### 6、min函数   `min`同`max`一样也属于数学用语,指提取指定范围中最小的数字,翻译为取最小值。   【例4-26】获取`numbers`列表中的最小值。 ```python numbers = [1, 3, 2, 1, 5, 4, 9] print('列表中的最小值', min(numbers)) ```   运行结果 ```python 列表中的最小值 1 ``` ### 7、sum函数   `sum`直接翻译就是求和的意思,可以计算指定元素的总和。   【例4-27】对`numbers`列表中的所有数据求和。 ```python numbers = [1, 3, 2, 1, 5, 4, 9] print('列表中元素的和', sum(numbers)) ```   运行结果 ```python 列表中元素的和 25 ``` ## 4.1.4 列表推导式   上一小节中讲解了可以通过各种内置函数对列表进行操作,那么如何快速的创建一个列表?因为我们不可能手动往列表里面添加大量数据!举个例子,我想创建一个列表,里面填充`1-10000`的数字,难道要手动写成`numbers = [1, 2, 3, 4, …,9999, 1000]?`我们可没有时间这么做,结合第3章学习的for循环逻辑和本章第1小结的`append`方法,假设要生成一个1-20的列表,则代码可以这么写。 ```python numbers = [] for i in range(1, 21): numbers.append(i) print(numbers) ```   运行结果 ```python [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] ```   上面的脚本中写了4句代码,仅仅是为了生成一个列表,里面填充了20个数字,似乎有点`大材小用`,能不能用一句代码来完成上述的功能?可以考虑使用列表推导式,也可以称为列表生成式,它是快速创建列表的一种常用写法,格式如下: ```python [表达式 for 变量 in 可遍历的类型] ``` ```python numbers = [x for x in range(1, 21)] print(numbers) ```   运行结果 ```python [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] ```   同学们会发现运行结果一致,但好像又没有太大的区别,感受不到列表推导式的`优越性`,那么不妨看一下推导式的第二种格式。 ```python [表达式 for 变量 in 可遍历的类型 if 筛选条件] ```   再举个例子,我想创建一个列表,里面填充的是`1-20`之间的所有偶数,代码该怎么写?如果结合第3章的if单分支结构,则代码可以这么写。 ```python numbers = [] for i in range(1, 21): if i % 2 == 0: numbers.append(i) print(numbers) ```   运行结果 ```python [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] ```   是不是感觉代码越写越麻烦?如果使用推导式,代码可以优化为以下写法: ```python numbers = [x for x in range(1, 21) if x % 2 == 0] print(numbers) ```   运行结果 ```python [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] ```   运行结果一致,但是写法似乎更简洁,用1行代码完成了4行代码才能做成的事,是不是很有成就感?   `注意`:代码行数不是越少越好,它并不能证明你很优秀,而是代码逻辑简洁、让其他人也能看懂,这才是一名`合格`的`coder`,毕竟鱼和熊掌不可兼得,代码行数和简洁性不可能都具备,同学们要做好两者间的平衡。 ## 4.1.5 列表应用   来综合练习一下4.1节讲解的列表相关知识。   【例4-28】根据下述要求完成代码的编写。 ```python (1)创建一个列表名为students,用于保存学生姓名,默认为空; (2)使用append方法,向students中追加张三、李四、王五三个字符串; (3)使用insert方法,向students中的第2个位置插入赵六,最后一个位置插入孙七; (4)使用索引修改第2个同学的名字为李小三; (5)创建一个列表名为numbers,使用列表推导式的形式为所有学生生成学号,要求格式为001、002、003等; (6)创建一个列表名为ages,默认填充若干年龄信息,依次为20、25、18、30、16; (7)使用推导式,获取并打印姓名以三结尾的学生的姓名; (8)打印年龄最大的学生的姓名; (9)计算所有学生的年龄平均值; (10)删除年龄最小的学生的姓名; (11)清空所有列表的内容; ``` ```python # (1) students = [] students = list() print(' (1)初始students列表', students) # (2) students.append('张三') students.append('李四') students.append('王五') print(' (2)添加学生姓名', students) # (3) students.insert(2, '赵六') students.insert(-1, '孙七') print(' (3)插入学生姓名', students) # (4) students[1] = '李小三' print(' (4)修改第2名同学姓名', students) # (5) numbers = ['%03d' % x for x in range(1, len(students) + 1)] print(' (5)生成学号', numbers) # (6) ages = [20, 25, 18, 30, 16] print(' (6)初始年龄列表', ages) # (7) print(' (7)查询以三结尾的学生姓名', [x for x in students if x.endswith('三')]) # (8) max_age = max(ages) max_age_index = ages.index(max_age) max_age_name = students[max_age_index] print(' (8)年龄最大的学生姓名', max_age_name) # (9) sum_age = sum(ages) avg_age = sum_age/len(ages) print(' (9)年龄平均值为', avg_age) # (10) min_age = min(ages) min_age_index = ages.index(min_age) students.pop(min_age_index) print(' (10)删除年龄最小的学生姓名', students) # (11) students.clear() ages.clear() numbers.clear() print(' (11)清空完毕', students, ages, numbers) ```   运行结果 ```python (1)初始students列表 [] (2)添加学生姓名 ['张三', '李四', '王五'] (3)插入学生姓名 ['张三', '李四', '赵六', '孙七', '王五'] (4)修改第2名同学姓名 ['张三', '李小三', '赵六', '孙七', '王五'] (5)生成学号 ['001', '002', '003', '004', '005'] (6)初始年龄列表 [20, 25, 18, 30, 16] (7)查询以三结尾的学生姓名 ['张三', '李小三'] (8)年龄最大的学生姓名 孙七 (9)年龄平均值为 21.8 (10)删除年龄最小的学生姓名 ['张三', '李小三', '赵六', '孙七'] (11)清空完毕 [] [] [] ``` # 4.2 元组   在4.1节中着重学习了列表的基本用法。本节的元组类型,其实也是常用的“容器”类型之一,它和列表的用法有很多相同之处,接下来展开学习。 ## 4.2.1 元组的基础操作   元组的基础操作和列表类似,也是关于创建、元素的增、删、改、读等操作,但是元组相比列表要更简单一些,各位可以放心学习。 ### 1、创建空元组   和列表一样,想要使用元组,必须先学会如何创建元组。接下来讲解2种创建空元组的形式。   (1)可以使用元组自身的构造方法来创建元组,代码如下所示: ```python students = tuple() ```   (2)使用`字面量`写法,用`()`来表示元组,代码如下所示: ```python students = () ```   上述的两种写法生成的元组被称为空元组,但很不幸的告诉大家,这两种写法以后都不会使用,后面会解释原因。 ### 2、创建带有元素的元组   元组的创建形式和列表形式类似,格式如下: ```python students = tuple(['张三', '李四', '王五']) ``` 或者 ```python students = ('张三', '李四', '王五') ``` 或者 ```python students = '张三', '李四', '王五' ```   `注意`:如果元组中只存放一个元素张三,那么必须写成`students = ('张三', )`,后面的`逗号必不可少`,否则它就不再是一个元组,而是这个元素本身的类型。 ### 3、向元组插入数据   `注意`:元组`不支持插入`操作! ### 4、读取元组数据   此操作和列表的读取形式一模一样,通过索引的形式来实现。   【例4-29】根据索引2读取`students`元组中的数据。 ```python students = ('张三', '李四', '王五') student = students[2] print('编号为2的姓名为', student) ```   运行结果 ```python 编号为2的姓名为 王五 ``` ### 5、修改元组数据   元组是不可变类型,列表是可变类型!一旦元组被创建,就不在允许被修改,更不要提增加、删除等操作。有同学会纳闷,既然它不可修改,那元组又有什么用呢?由于它的不可修改特性,一般在调用函数时可以通过元组来传递参数,用于防止有人企图在函数逻辑中篡改参数的内容,因为元组相对于列表而言更安全。   `说明`:函数在第6章会进行讲解。   【例4-30】修改`students`元组中第0个元素为`张小三`。 ```python students = ('张三', '李四', '王五') students[0] = '张小三' print('企图修改元组的元素内容', students) ```   运行结果 ```python Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment ```   从运行结果来看,企图修改元组内容就会直接报错,进一步验证了上述结论。虽然元组中各元素不允许通过赋值的形式修改,但如果这个元素本身是可变类型,比如列表、字典,其实也可以进行修改。   【例4-31】向`students`元素中的列表追加元素`孙七`。 ```python students = ('张三', '李四', ['王五', '赵六']) students[2].append('孙七') print('企图修改元组的可变类型元素内容', students) ```   运行结果 ```python 企图修改元组的可变类型元素内容 ('张三', '李四', ['王五', '赵六', '孙七']) ```   从结果来看,元组中的列表中确实新增了孙七这个元素,但要说明的是,不可修改的特性指的是不允许对元组中的元素进行赋值操作,而不是一点都不能改。举个例子,你的身份证代表了你的身份,身份证上永远是你的名字,不允许改为其他人的名字,但是你自己可以吃饭、睡觉等。 ## 4.2.2 元组的组包与拆包   在上一小节中曾出现过一句代码为`students = '张三', '李四', '王五'`,其实这就是元组的`组包`。当给一个变量赋值的时候,如果`=号`右边有好几个值且用逗号隔开,则会将所有元素自动组装为一个整体。   `拆包`与组包正好相反,就是将一个整体拆分成若干个部分然后分别赋值给不同的变量。   【例4-32】将`张三`、`男`、`25`进行`组包`和`拆包`。 ```python infos = ('张三', '男', 25) name, sex, age = infos print('姓名为', name) print('性别为', sex) print('年龄为', age) ```   运行结果 ```python 姓名为 张三 性别为 男 年龄为 25 ```   `注意`:拆包的时候`=号`左边的变量数量要和`=号`右边的拆解个数保持一致,否则就有可能会报错。   【例4-33】将包含`张三`的元组拆包。 ```python infos = ('张三', ) name, sex = infos print('姓名为', name) print('性别为', sex) ```   运行结果 ```python Traceback (most recent call last): name, sex = infos ValueError: not enough values to unpack (expected 2, got 1) ```   报错原因的意思就是:期望2个返回值,但只得到了一个,说白了就是拆包与组包的数量对不上。为什么说可能报错?因为还可以通过`*`号表达式(`starred expressions`)来解决数量不一致的问题。   【例4-34】使用星号表达式接收`infos`元组中的数据。 ```python infos = ('张三', '男', 20) name, *other = infos print('姓名为', name) print('其它为', other) ```   运行结果 ```python 姓名为 张三 其它为 ['男', 20] ```   `注意`:`*`号只能加到某一个变量之前,用来表示忽略多余的元素。 ## 4.2.3 元组和列表的区别   用一个表格来对比元组和列表2个类型之间的区别,如下表4-1所示。 表4-1 列表和元组的区别 | 类型 | 可变性 | 相同点 | 安全性 | 访问速度 | | --- | --- | --- | --- | --- | | 列表 (list) | 可变类型,可以增、删、改 | 都是容器类型,有序、可以互相转化| | | 元组 (tuple)| 不可变类型,一旦定义就不可更改 | 更高 | 更快 | # 4.3 字典   前2个小节已经接触并学习了两种常见的容器类型,分别是列表和元组,各有各的特点和用途,本节将要学习的字典类型与上述两种类型类似,也是容器类型,也很常用。   `字典(dict)`,和日常生活中使用的字典类似。平常使用的新华字典通常由两部分组成,一个是目录,一个是内容;而`Python`中的字典类型也由两部分组成,分别是键(`key`)和值(`value`),也称之为键值对。可以通过目录找到正文,也可以通过`key`找到`value`。 ## 4.3.1 创建字典的基本操作   创建字典也可以分为2种形式,空字典和带有元素的字典。 ### 1、创建空字典。   (1)使用字典自身的构造方法来创建,代码如下所示: ```python info = dict() ```   (2)使用字面量{}来表示空字典,代码如下所示: ```python info = {} ``` ### 2、创建带有元素的字典。   在本节开始的时候已经说过,字典应该由`key`和`value`键值对组成,所以创建字典的时候可以按照下面的格式进行创建。   【例4-35】通过键值对创建`info`字典。 ```python info = dict({ '姓名': '张三', '年龄': 25, '性别': '男', }) ```   通过字面量创建字典,如下所示: ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } ```   通过关键字创建字典,如下所示: ```python info = dict( 姓名='张三', 年龄=25, 性别='男', ) ```   通过fromkeys创建字典,如下所示: ```python info = dict.fromkeys('abcd', '1') ```   从上述脚本的编写可知,在创建字典的时候,为了能更直观的观察字典结构,一般会要求每个键值对单独一行,多个键值对之间用逗号隔开。在上述代码中,姓名、年龄、性别就是字典的`key`,张三、25、男就是字典的`value`。就像新华字典一样,通过目录快速找到对应的内容,当然也可以通过`key`,快速找到对应的`value`。   此处的`key`,要求是不可变类型,包括但不限于字符串、元组等。 ## 4.3.2 字典的基本操作   字典类型的操作相对来讲比较简单,包括增、删、改、读等四个操作,但是增和更操作是同一个写法,相信各位可以很快掌握。 ### 1、向字典插入/更新数据   向字典添加数据和更新数据的方法是一样的,可以通过`字典变量[key] = value`的形式来实现。如果`key`不存在,则表示添加数据,如果`key`存在,则表示更新数据。   【例4-36】向`info`字典中更新年龄并添加手机号。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } print('添加/更新前', info) info['手机号'] = '12512345678' info['年龄'] = 30 print('添加/更新后', info) ```   运行结果 ```python 添加/更新前 {'姓名': '张三', '年龄': 25, '性别': '男'} 添加/更新后 {'姓名': '张三', '年龄': 30, '性别': '男', '手机号': '12512345678'} ```   也可以通过字典内置的`update`方法进行批量添加/更新操作。   【例4-37】向`info`字典更新姓名并添加手机号。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } other = { '姓名': '张小三', '年龄': 50, '手机': '13512345678' } print('批量添加/更新前', info) info.update(other) print('批量添加/更新后', info) ```   运行结果 ```python 批量添加/更新前 {'姓名': '张三', '年龄': 25, '性别': '男'} 批量添加/更新后 {'姓名': '张小三', '年龄': 50, '性别': '男', '手机': '13512345678'} ``` ### 2、读取字典数据   通过`字典变量[key]`的形式进行数据的访问,你提供`key`,则返回对应的`value`。   【例4-38】根据`姓名`读取`info`字典中的内容。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } key = '姓名' value = info[key] print('key为', key) print('value为', value) ```   代码不复杂,但却存在一个问题。如果你的`key`在字典中不存在会出现什么问题? ```python key = '手机号' value = info[key] print('key为', key) print('value为', value) ```   运行结果 ```python Traceback (most recent call last): value = info[key] KeyError: '手机号' ```   报错信息为手机号这个`key`不存在。为了防止这种特殊情况的出现,推荐使用`字典变量.get(key, 默认值)`。这种写法的好处在于,如果`key`存在,则返回对应的`value`;如果`key`不存在,则返回默认值。如果没有指定默认值,则返回`None`。   【例4-39】更安全的访问`info`字典中的`手机号`。 ```python key = '手机号' value1 = info.get(key) value2 = info.get(key, '没有手机号') print('key为', key) print('value1为', value1) print('value2为', value2) ```   运行结果 ```python key为 手机号 value1为 None value2为 没有手机号 ```   字典类型内部还自带了三个方法可以获取更多数据,包括`items`、`keys`、`values`。   【例4-40】字典包含的内置方法。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } print('所有的keys', info.keys()) print('所有的values', info.values()) print('所有的items', info.items()) ```   运行结果 ```python 所有的keys dict_keys(['姓名', '年龄', '性别']) 所有的values dict_values(['张三', 25, '男']) 所有的items dict_items([('姓名', '张三'), ('年龄', 25), ('性别', '男')]) ```   从运行结果中可以看到,`items`可以将字典的键值对以元组的形式进行返回,其它两个方法作用已经很明显,此处不再阐述。 ### 3、删除字典数据   字典的删除同列表类似,也可以通过`pop`和`del`两种方式删除,但是`pop`的用法稍微不太一样。   【例4-41】通过`pop`方法删除`info`字典中的`性别`。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } print('删除前', info) info.pop('性别') print('删除后', info) ```   运行结果 ```python 删除前 {'姓名': '张三', '年龄': 25, '性别': '男'} 删除后 {'姓名': '张三', '年龄': 25} ```   那万一删除的字段不存在呢?作为优秀的编程人员,考虑问题要全面! ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } print('删除前', info) info.pop('手机') print('删除后', info) ```   运行结果 ```python Traceback (most recent call last): info.pop('手机') KeyError: '手机' ```   果然会抛出异常!请各位同学思考一下,怎么做才能顺利删除指定字段?可以结合if分支结构来辅助删除。   【例4-42】通过pop方法更安全的删除`info`字典中的`手机`。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } key = '手机' if key in info: info.pop(key) print('删除成功', info) else: print('删除失败', info) ```   运行结果 ```python 删除失败 {'姓名': '张三', '年龄': 25, '性别': '男'} ```   当然也可以借助`del`关键字删除指定键值对,但是也要考虑全面。   【例4-43】通过`del`方法更安全的删除`info`字典中的`姓名`。 ```python info = { '姓名': '张三', '年龄': 25, '性别': '男', } key = '姓名' if key in info: del info[key] print('删除成功', info) else: print('删除失败', info) ```   运行结果 ```python 删除成功 {'年龄': 25, '性别': '男'} ```   也可以通过`clear`方法直接清空整个字典,用法和列表清空类似,不再阐述。 ## 4.3.3 字典推导式   和列表类型一样,字典也可以通过推导式的形式快速生成一个字典。 ```python info = {key: value for key, value in [('张三', 20), ('李四', 15)]} print('字典推导式', info) ```   运行结果 ```python 字典推导式 {'张三': 20, '李四': 15} ```   需要提醒的是,不管是列表推导式,还是字典推导式,它的逻辑是有限的,无法实现很复杂的代码逻辑。 # 4.4 集合   在前3个小节中已经介绍并阐述了最常用的容器类型,在以后的项目案例中会很频繁的碰到。但是本节讲解的集合类型,相对来讲用的并不是很频繁,只在某些特定的情况下才会选择使用,它和字典有相似之处,一起来看一下。 ## 4.4.1 集合的基础操作   集合和列表、字典一样,都属于可变数据类型,因此相关的操作也类似。 ### 1、创建空集合   (1)使用集合自身的构造方法来创建,代码如下所示: ```python info = set() ```   (2)使用字面量{}来表示空集合,代码如下所示: ```python info = {} ```   从上述脚本的写法会发现,空集合的字面量写法和空字典的字面量写法是相同的,而Python默认会将{}认定为是字典,故空集合不能使用字面量的形式创建。 ### 2、创建带有元素的集合   集合中只能存放字符串、数字、元组等不可变类型的数据,数据与数据之间用逗号隔开。   创建带有数据的集合,如下所示: ```python info = set({'张三', 20, '河南'}) ``` 通过字面量创建集合,如下所示: ```python info = {'张三', 20, '河南'} ``` 如果集合中添加了可变数据类型会怎么样? ```python info = set({'张三', [20, ], '河南'}) ```   运行结果 ```python Traceback (most recent call last): info = set({'张三', [20, ], '河南'}) TypeError: unhashable type: 'list' ```   错误提示中的`unhashable`翻译过来为不可哈希类型,简单来说可变类型都是不可哈希类型;相反的,不可变类型都是可哈希类型。 ### 3、向集合插入数据   集合主要通过add和update两个方法进行数据的添加操作。   【例4-44】使用add方法向info集合中添加“王五”。 ```python info = {'张三', '李四'} print('集合添加前', info) info.add('王五') print('集合添加后', info) ```   运行结果 ```python 集合添加前 {'李四', '张三'} 集合添加后 {'李四', '张三', '王五'} ```   也可以通过update方法批量添加元素。   【例4-45】使用update方法向info集合中批量添加“王五”、“赵六”、“张三”。 ```python info = {'张三', '李四'} print('批量添加前', info) other = {'王五', '赵六', '张三'} info.update(other) print('批量添加后', info) ```   运行结果 ```python 批量添加前 {'张三', '李四'} 批量添加后 {'赵六', '李四', '张三', '王五'} ```   从输出结果能看到,集合的特点是可变、无序且数据互不重复的。   `update`方法中除了添加集合作为参数,也可以是列表、元组、字符串等类型,代码类似,不再阐述。 ### 4、读取集合数据   集合类型并不能直接读取某个数据,只能借助循环遍历的形式读取所有数据。   【例4-46】循环遍历集合numbers中的内容。 ```python numbers = {1, 2, 3, 'a', 'b', 'c'} print(numbers) for number in numbers: print(number) ```   为什么集合不能像列表一样通过索引的方式取某一个数据呢?因为它是无序的! ### 5、修改集合数据   很遗憾,集合保存的都是不可变类型的元素,而且集合还是无序的,所以目前它不具备修改的能力。 ### 6、删除集合数据   删除集合中的数据可以使用remove方法或discard方法,两个方法都能实现删除功能,那又有什么区别?下面举例来说明。   【例4-47】remove和discard分区别。 ```python info = {'张三', '李四', '王五', '赵六'} print('使用discard删除前', info) info.discard('李四') print('使用discard删除后', info) print('使用remove删除前', info) info.remove('赵六') print('使用remove删除后', info) ```   运行结果 ```python 使用discard删除前 {'张三', '赵六', '李四', '王五'} 使用discard删除后 {'张三', '赵六', '王五'} 使用remove删除前 {'张三', '赵六', '王五'} 使用remove删除后 {'张三', '王五'} ```   如果要删除的字段不存在呢? ```python info = {'张三', '李四', '王五', '赵六'} print('使用discard删除前', info) info.discard('李四1') print('使用discard删除后', info) print('使用remove删除前', info) info.remove('赵六1') print('使用remove删除后', info) ```   运行结果 ```python 使用discard删除前 {'赵六', '李四', '王五', '张三'} 使用discard删除后 {'赵六', '李四', '王五', '张三'} 使用remove删除前 {'赵六', '李四', '王五', '张三'} Traceback (most recent call last): info.remove('赵六1') KeyError: '赵六1' ```   从输出结果来看,`remove`和`discard`唯一的区别就是,当删除的数据不存在的时候,`remove`会抛出异常。   最后,也可以通过`clear`方法删除整个集合。 ## 4.4.2 集合推导式   和列表的推导式几乎一样,区别就是将[]换成了{}而已,格式如下: ```python 集合变量 = {表达式 for 变量 in 可遍历的类型 if 筛选条件} ```   【例4-48】过滤students列表中重复的“张三”。 ```python students = ['张三', '李四', '张三', '王五'] print('原始数据', students) info = {x for x in students} print('处理后数据', info) ```   运行结果 ```python 原始数据 ['张三', '李四', '张三', '王五'] 处理后数据 {'王五', '张三', '李四'} ```   其实也没有这么麻烦,可以通过类型转换的方式直接实现。 ```python students = ['张三', '李四', '张三', '王五'] print('原始数据', students) info = set(students) print('处理后数据', info) ``` # 4.5 切片(Sliceing)的使用   `切片`,就像字面意思理解的那样,将一个“东西”切成好几片。只不过这个“东西”就是本章学习过的列表、元组等类型,包括第2章学习过的字符串也可以被切片。其语法如下: ```python 序列[start:end:step] ```   `start`表示开始位置下标,`end`表示结束位置下标,`step`表示步长且默认为1。 ## 4.5.1 字符串切片   字符串中的每个字符对应的索引值从前往后数其值从0开始依次+1;从后往前数是从-1开始依次-1,如图4-2所示。    【例4-49】使用切片对`sentence`字符串进行读取 ```python sentence = 'abcdef' print('原始数据', sentence) print('start=2,step=1', sentence[2:]) print('start=-1,step=1', sentence[-2:]) print('start=2,end=4,step=1', sentence[2:4]) print('start=-4,end=-2,step=1', sentence[-4:-2]) print('start=2,end=40,step=1', sentence[2:40]) print('start=-20,end=-1,step=1', sentence[-20:-1]) print('start=0,end=2,step=1', sentence[:2]) print('start=0,end=-2,step=1', sentence[:-2]) print('start=1,end==4,step=2', sentence[1:4:2]) print('start=0,step=2', sentence[:]) print('start=0,step=-1', sentence[::-1]) ```   运行结果 ```python 原始数据 abcdef start=2,step=1 cdef start=-1,step=1 ef start=2,end=4,step=1 cd start=-4,end=-2,step=1 cd start=2,end=40,step=1 cdef start=-20,end=-1,step=1 abcde start=0,end=2,step=1 ab start=0,end=-2,step=1 abcd start=1,end==4,step=2 bd start=0,step=2 abcdef start=0,step=-1 fedcba ``` ## 4.5.2 列表切片   列表的切片同字符串切片类似,只不过变量类型从字符串变为了列表而已。   【例4-50】使用切片访问students列表中的元素 ```python students = ['孙七', '张三', '李四', '周八', '王五', '赵六'] print('start为1', students[1:]) print('end为3', students[:3]) print('start为1且end为3', students[1:3]) print('start为1且end为30', students[1:30]) print('start为3且end为1', students[3:1]) print('start为-3', students[-3:]) print('start为-3且end为-1', students[-3:-1]) print('start为-1且end为-3', students[-1:-3]) print('start为-5且end为-30', students[-5:-30]) print('start和end均不填', students[:]) ```   运行结果 ```python start为1 ['张三', '李四', '周八', '王五', '赵六'] end为3 ['孙七', '张三', '李四'] start为1且end为3 ['张三', '李四'] start为1且end为30 ['张三', '李四', '周八', '王五', '赵六'] start为3且end为1 [] start为-3 ['周八', '王五', '赵六'] start为-3且end为-1 ['周八', '王五'] start为-1且end为-3 [] start为-5且end为-30 [] start和end均不填 ['孙七', '张三', '李四', '周八', '王五', '赵六'] ``` ## 4.5.3 切片的特点   通过观察输出结果可以发现:start和end可以为正数、也可以为负数、甚至不填,都可以得到相应的结果,特点如下:   (1)如果start不写,则默认从0开始;   (2)如果end不写,则默认到最后结束;   (3)如果start和end都写,则从start开始,到end-1结束;   (4)如果start和end都不写,则表示复制。复制的意思是创建一个全新的变量,且内容和原始数据一模一样。   (5)如果start>=end,则返回空;   (6)如果start越界或end越界,均不会抛出异常;   (7)如果start和end都不写,步长设置为-1,则表示反向。 # 4.6 案例:实现终端版的用户管理系统   随着互联网的发展各行业已进入信息时代,数字技术的快速发展衍生出数字经济,例如在线支付平台、疫情期间的大数据分析等,而用户管理系统几乎是所有软件必备的功能,例如支付宝、QQ、抖音、新浪微博等。只有用户进行了登录,才能够进行相应的个性化操作。在本系统中,将模拟对用户信息的一个基本管理流程,包括查询、删除、创建等操作。该程序将使用本章学习的列表为主要容器类型,结合第3章学习过的for循环、 while循环、if分支等结构,还有第2章学习过的输出语句、格式化字符串、数据输入等知识进行实现,对各位的编程能力和举一反三的能力是一种考验。在下文中将对本项目的流程进行讲解。   【例4-51】用户管理系统 ```python users, passwds = ['root'], ['admin'] user = input("输入你的登录用户名:") passwd = input("输入你的登录密码:") choice = "" if user == users[0] and passwd == passwds[0]: while True: print("""******用户管理系统******\n1-添加用户信息\n2-删除用户信息\n3.查看用户信息\n4.退出""") if not choice: choice = input("请输入要做的操作:") if choice.isdigit(): if choice == '1': print("添加用户信息".center(50, "*")) new_user = input("请输入要添加的用户名:") if new_user in users: print("用户已存在,请重新输入!") choice = "1" else: new_passwd1 = input("请输入用户密码:") new_passwd2 = input("请输入用户确认密码:") if new_passwd1 and new_passwd1 == new_passwd2: print(f"添加{new_user}成功".center(20, '*')) users.append(new_user) passwds.append(new_passwd1) choice = '' else: print("密码有误!请检测是否输入密码或两个密码是否一致") choice = "1" elif choice == '2': print('删除用户信息'.center(50, "*")) del_user = input('请输入要删除的用户名称:') if del_user not in users: print('删除的用户不存在!请重新输入!') choice = '2' elif del_user == users[0]: print('不能删除管理员账户!请重新输入') choice = '2' else: idx = users.index(del_user) users.pop(idx) passwds.pop(idx) print('删除成功!') choice = '' elif choice == '3': print("查看用户信息".center(50, '*')) for idx,(use,psw) in enumerate(zip(users, passwds)): print(f"编号{idx+1},用户名{use},密码{psw}") choice = '' elif choice == '4': break else: print("您所需要的功能在开发中,请重新选择!") choice = '' else: print("输入错误!请重新输入!") print("感谢使用本系统!期待您的下次使用!".center(50, "*")) ```   本项目具有的功能主要包括:超级管理员登录、添加普通用户、删除用户、查看用户、退出等5个功能,具体流程如图4-3所示:    1.设定管理员用户名为root,密码为admin,其具有最高权限,只有他可以进行用户的管理,且其不能被删除。   2.如果登录的是管理员,则界面提示所有功能,如图4-4所示;若不是管理员则提示登录失败,如图4-5所示。     3.用户选择1,则提醒用户输入新增的用户名和密码。若用户名不存在且密码无误,则提示“添加xxx成功”,如图4-6所示;若用户名存在,则提示“用户已存在,请重新输入!”,如图4-7所示;若密码为空或密码与确认密码不一致,则提示“密码有误!请检测是否输入密码或两个密码是否一致”,如图4-8所示。      4.用户选择2,则提醒用户输入要删除的用户名。若用户名不存在,则提示“删除的用户不存在!请重新输入!”,如图4-9所示;若用户名存在但是管理员,则提示“不能删除管理员账户!请重新输入”,如图4-10所示;若用户名存在但不是管理员,则提示“删除成功!”,如图4-11所示。      5.用户选择3,则打印当前所有的用户名和密码,前面自动加编号,如图4-12所示。    6.用户选择4,则提示“感谢使用本系统!期待您的下次使用!”并直接退出程序,如图4-13所示。  # 4.7 本章小结   本章介绍了Python中常用的高级数据结构,包括可变类型:列表、字典、集合等;不可变类型:元组等。详细介绍了各类型的特点、作用、常用方法等,包括如何创建、数据的增/删/改/读、各类型内置的方法,还有切片等技术。同时还通过一个上机案例对大部分的知识点进行了复习与实践,为后续学习更高难度的知识进行铺垫。 # 4.8 思考练习 ## 4.8.1 单选题 1. 创建列表是对应的字面量写法是? A.[ ] B.{ } C.( ) D.< > 2. 读取列表元素时,倒数第2个元素对应的索引是? A.2 B.-2 C.3 D.-3 3. 在列表自带的方法中,哪个方法可以统计列表存放的元素个数? A.sum B.counter C.number D.count 4. 在本章中,Index翻译为? A.顺序 B.位置 C.索引 D.首页 5. 列表在使用pop删除元素时,默认删除的是? A.第0个元素 B.第1个元素 C.随机1个元素 D.最后一个元素 6. 假如1个列表中只有5个元素,那么元素的索引最大为? A.5 B.4 C.6 D.7 ## 4.8.2 多选题 1. Python的可变类型包括? A.list B.tuple C.dict D.set 2. 列表可以通过以下哪些方式添加元素? A.insert B.append C.extend D.+ 3. 以下哪些类型可以作为字典的key? A.字符串 B.数字 C.元组 D.列表 4. 现有字典变量`a={'name': '张三'}`,若想获取张三这个内容,则写法可以是? A.name=a['name'] B.name=a.pop('name') C.name=a.get('name') D.del a['name'] ## 4.8.3 填空题 1. 在Python中,设有`pages=[1, 2, 3, 4, 5]`,则`pages[1]`值为 ______;`pages[2:2]`值为 ______;`pages[-3:]`值为 ______;`pages[4:2]`值为 ______;`pages[1::2]`值为 ______;pages[::-1]值为 ______。 2. 列表的`sort`排序默认是____序(填升或降)。 3. 元组对应的单词是________(字母全小写)。 4. 列表的字面量写法是________,字典的字面量写法是__________(填写符号,不加空格)。 5. 现有脚本`a, b= 10, 20`,则`a`=______,`b`=______;若`a=1,b=2,a,b=b, a`,则`a`=______,`b`=_______。 6. 字典由______和_______组成(填写单词且均小写)。 ## 4.8.4 判断题 1. 列表只能存放同一种类型的数据。( ) 2. 元组中的元素不能被赋值。( ) 3. 元组中的某些元素可以被修改。( ) 4. 元组不能使用切片功能。( ) 5. 从读取数据的角度讲,元组比列表的效率更高。( ) 6. 使用字符串的时候用单引号效率会更高。( ) ## 4.8.5 编程题 1. 使用列表推导式生成一个由1-10的平方组成的列表。 示例输出:`[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]` 2. 使用列表推导式生成如下列表`[1, 4, 27, 256, 3125, 46656, 823543, 16777216, 387420489]`。 提示:`1=1*1,4=2*2,27=3*3*3,256=4*4*4*4` 3. 选择一个合适的数据类型,保存以下三个信息:姓名为张三,年龄20,性别男。 4. 现在有三名同学,分别叫张三、李四、王五,三人的数学成绩分别是50、90、75,语文成绩分别是89、67、90,请计算数学和语文成绩的平均分(小数保留2位数)。 提示:列表套字典+for循环 5. 生成一个包含数字`1,2,3,…,30`的列表(不包括30),输出列表的值;输入一个2~9之间的正整数,查找列表中是否存在有这个数的倍数和数位上包含这个数字的数,若存在,将其从列表中删除,输出删除后的列表。 示例输入:`3` 示例输出:`[1, 2, 4, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22, 25, 26, 28, 29]` 提示:while循环+if条件+列表推导式
张泽楠
2024年6月18日 18:02
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码