--- title: List and Tuple 2 tags: list, tuple --- # List and Tuple Tutorial 2 **难度**: 1 **时长**: 45 min --- ## 成员包含 `in` 1. `[1,2,5,7]` 是否包含 `2` 呢?怎么实现? 2. 文件属性 Permissions = `"rw"`,检查其是否可写,是否可执行? 3. 检查输入用户名是否为合法 `users = ['mlh', 'foo', 'bar']` ## `for ... in` 循环 ### 打印每个成员 最简单的情况是不需要写索引的: ```python a = [1,2,3,4,5,6] for each in a: print(each) ``` ## 案例2: 将偶数位置的成员值乘 10 ### 写法1: 人工维持 index 自增 for ... in 循环 + 人工维持 index 自增 ```python a = [1,2,3,4,5,6] index = 0 for each in a: if index % 2 == 0: a[index] = each *10 index += 1 print(a) ``` ### 写法2: 经典写法 range(len) 此方法需要提前知道 a 的长度: ```python a = [1,2,3,4,5,6] for index in range(len(a)): if index % 2 == 0: a[index] = each *10 print(a) ``` 下面的 enumerate() 写法更简洁。 ### 写法3: enumerate() 让临时索引不那么可见: ```python a = [1,2,3,4,5,6] for index, each in enumerate(a): if index % 2 == 0: a[index] = each *10 ``` 好处是代码短了2行,而且是容易出错的两行。一个是初始化,另一个是 index 的自增。这两个属于固定动作,现在用 `enumerate()` 给自动化了。 也可以很紧凑地用下面的代码: ``` a[::2] = [0] * 3 ``` ### zip() 用法 zip() 和 enumerate() 用法是类似的,不同的是将序号换成了一个列表。例如,并行打印两个列表中的名字与年龄: ``` name = ['Tom', 'Alice', 'Bob'] age = [30, 42, 35] for name, age in zip(name, age): print(name, age) ``` ### ### 列表推导式 (List Comprehension) 传统写法示例: ```python # 传统方法 squares = [] for x in range(10): squares.append(x**2) ``` 更简洁的列表创建方式: ```python # 列表推导式 squares = [x**2 for x in range(10)] ``` **示例**:将偶数位置的成员值乘 10 ```python # 传统方法 a = [1,2,3,4,5,6] for index, each in enumerate(a): if index % 2 == 0: a[index] = each * 10 ``` 简洁写法: ``` # 列表推导式 a = [x * 10 if i % 2 == 0 else x for i, x in enumerate(a)] ``` --- **列表示推导的语法特点**: - 更简洁、可读性更好 - 性能通常优于传统循环 - 支持条件过滤:`[x for x in range(10) if x % 2 == 0]` - 支持嵌套循环 ### [练习2]: List for-in 修改 **题目**:学生成绩处理 ```python scores = [85, 92, 78, 96, 88, 76, 95, 89] ``` 1. 将不及格分数(<60)替换为60 2. 将前3个成绩提高10% 3. 使用enumerate()打印每个学生的序号和成绩 [答案] ``````{only} show_answer ```python scores = [85, 92, 78, 96, 88, 76, 95, 89] # 1. 替换不及格分数 for i in range(len(scores)): if scores[i] < 60: scores[i] = 60 # 2. 前3个成绩提高10% for i in range(3): scores[i] = int(scores[i] * 1.1) # 3. 使用enumerate打印 for index, score in enumerate(scores): print(f"学生{index+1}: {score}分") ``` `````` ## 列表赋值: 指向同一对象(副作用) 在仿真通信系统时,接收端的信号 `recv_signal` 引用了发送端发送的信号 `send_signal`,并做了一些修改处理,却意外地发现发送信号也发生了变化: ```python >>> send_signal = [1,2,3,4,5] >>> recv_signal = send_signal >>> recv_signal[2]=10 #2 修改接收信号 >>> print(send_signal) [1, 2, 10, 4, 5] ``` 总结哪里有问题!!! 如何解决和避免? ### 改法1 ```python >>> send_signal = [1,2,3,4,5] >>> recv_signal = send_signal.copy() >>> recv_signal[2]=10 #2 修改接收信号 >>> print(send_signal) ``` ### 改法2 ```python >>> send_signal = [1,2,3,4,5] >>> recv_signal = send_signal[:] >>> recv_signal[2]=10 #2 修改接收信号 >>> print(send_signal) ``` 以上哪个方法正确? 你还可以用 `id()` 探索一下这几种情况下 `send_single` 和 `recv_signal` 之间的关系 ## 综合练习 ### [练习3]: 列表成员查找 已知有一个列表 `a`,包含了一组学生的数据(姓名,学号),要求写一个程序,完成以下功能: 1. 能够根据姓名或学号,查询到学生的完整记录并返回。 2. 根据姓名,或者学号排序 数据样表: ```python a = [('张三', 1001), ('李四', 1002), ('王五', 1003), ('陈六', 1004), ...] ========================= Input: '王五' Output: ('王五', 1003) ``` **思路**:把每一行的记录都找出来,看每一行的第一个字段是姓名是什么是,是不是要找的名字,如果是,则打印结果。 **示例**: 通过学生「姓名」查找 ```python a = [('张三', 1001), ('李四', 1002), ('王五', 1003), ('陈六', 1004),] for student in a: print(student) print(student[0], student[1]) if student[0] == '王五': print(student) ``` **练习**: 通过修改上面的代码,完成通过学号查找 **思考**:如何评价查找计算量?其计算量有多大? ### [练习4]: 列表条件筛选 **题目**:学生信息管理系统(结合列表推导式) ```python students = [ ('张三', 18, 85), ('李四', 19, 92), ('王五', 20, 78), ('赵六', 18, 96) ] ``` 1. 使用列表推导式获取所有学生的姓名 2. 获取年龄大于18岁的学生信息 3. 计算平均成绩 **要求**: 均使用列表推导式实现,除开打印外,其他代码只能用一行。 [答案] ``````{only} show_answer ```python students = [ ('张三', 18, 85), ('李四', 19, 92), ('王五', 20, 78), ('赵六', 18, 96) ] # 1. 获取所有学生姓名 names = [student[0] for student in students] print(names) # ['张三', '李四', '王五', '赵六'] # 2. 获取年龄大于18岁的学生 older_students = [student for student in students if student[1] > 18] print(older_students) # [('李四', 19, 92), ('王五', 20, 78)] # 3. 计算平均成绩 average_score = sum(student[2] for student in students) / len(students) print(f"平均成绩: {average_score}") # 平均成绩: 87.75 ``` ``````