跳到主要内容
Python 程序设计 · 在线教材适用于 2026—2027 学年第一学期

第 6 章 · 从计数走向具体成员

列表与报名名单管理

前几章能判断报名是否成功,却没有留下“到底是谁报名”的证据。本章用一个有顺序、可修改的列表记录姓名:录入时拒绝空名和重复,取消前确认成员存在,查询不改变数据,统计与排序展示都从同一份名单产生。

预计阅读
60 分钟
上机实践
90 分钟
前置知识
第 1—5 章
本章产出
个人报名名单管理器

学完本章,你应该能够

  • 创建、访问并修改列表元素,说明索引从 0 开始以及空列表的边界。
  • 正确使用 append()、remove() 与 in,拒绝空姓名和重复姓名。
  • 使用 for 遍历名单,区分元素值、列表索引和展示编号。
  • 解释原地修改与产生新列表的区别,用 sorted() 展示而不篡改报名顺序。
  • 独立完成录入、取消、查询、统计和八组状态测试。

6.1 为什么人数不等于名单

假设屏幕只显示“报名 3 人”。它不能回答陈晨是否已报名,也不能判断下一次输入是否重复。把姓名分别写进 name1、name2、name3 也不灵活:人数变化就要改变量数量和处理代码。列表把同类数据放在一个容器中,并保留添加的先后顺序。

one_name_or_many.py
student_name = "陈晨"
student_name = "王宁"
members = ["陈晨", "王宁"]
print(student_name)
print(members)
print(len(members))
运行结果
王宁
['陈晨', '王宁']
2

第二次赋值使单个变量只剩王宁;列表仍保留两位成员。len(members) 根据实际名单计算人数,不必另设一个可能与名单脱节的计数器。本章暂以“清洗后的姓名”识别成员;真实场景有同名者,这一限制会在第 7 章用学号解决。

6.2 列表、索引与修改

用方括号创建列表,逗号隔开元素。第一个元素的索引是 0,最后一个也可用 -1 表示。对姓名列表来说,一个元素是完整姓名,不是一个汉字。列表可修改:给某个有效索引赋新值,长度不会变化;这与第 5 章不能原地改字符的字符串不同。

list_index.py
members = ["陈晨", "王宁"]
print(members[0], members[-1])
members[1] = "李悦"
print(members)
print(len(members))
运行结果
陈晨 王宁
['陈晨', '李悦']
2

这里的修改是“把第二个格子的值换掉”,不是新增一位;新的名单仍有两人。members[2] 在这个例子里会抛出 IndexError,因为有效的正索引只有 0 和 1。不要把展示给人的“第 1 位”误当作索引 1。

6.3 空列表也是合法状态

程序从 members = [] 开始。此时长度为 0,成员查询会得到 False,遍历会执行零次;但访问 members[0] 会越界。先判断是否有元素,再访问首位。

empty_list.py
members = []
print(len(members))
print("陈晨" in members)
if len(members) > 0:
    print(members[0])
else:
    print("暂无成员")
运行结果
0
False
暂无成员
不要用假成员填空

如果让空名单预先含有 "暂无成员",len() 就会把提示语当作一个报名者。空状态要用空列表表示,提示语只负责输出。

6.4 新增、删除与去重:先检查,再改变

append(value) 在列表末尾添加一个元素;remove(value) 按值删除第一个匹配元素。两者都会修改原列表,返回值都不是新名单。删除不存在的值会抛出 ValueError,所以先用 in 判断。

add_remove.py
members = ["陈晨", "王宁"]
name = " 李悦 ".strip()
if name != "" and name not in members:
    members.append(name)
print(members)
if "陈晨" in members:
    members.remove("陈晨")
print(members)
运行结果
['陈晨', '王宁', '李悦']
['王宁', '李悦']

清洗要发生在去重之前:" 李悦 " 和 "李悦" 应比较清洗后的姓名。判断优先级是“结束哨兵 → 空姓名 → 重复姓名 → 新增”;取消则是“空输入可跳过 → 存在才删除”。若重复输入被拒绝,名单和人数都应保持不变。

原地修改的常见坑

不要写 members = members.append(name) 或 members = members.remove(name)。方法已修改原列表,返回的 None 若被赋给 members,后续就不能再按列表使用了。第 6 周基础版在录入时拒绝重复姓名,因此 remove() 只会遇到一个匹配者。

6.5 遍历每个人,编号从 1 显示

如果只需要姓名,用 for name in members;如果还需要在屏幕上编号,可用 range(len(members)) 得到索引 0、1、2,再显示 index + 1。这里的编号只是界面文字,不改变列表内部索引。

show_members.py
members = ["陈晨", "王宁", "李悦"]
for name in members:
    print("成员:", name)
for index in range(len(members)):
    print(f"{index + 1}. {members[index]}")
运行结果
成员: 陈晨
成员: 王宁
成员: 李悦
1. 陈晨
2. 王宁
3. 李悦

此处是顺序展示。若取消了陈晨,后面成员的位置会前移,新的第 1 位就是王宁;不应继续沿用旧编号。

6.6 排序只用于展示,报名顺序仍要保留

sorted(members) 返回按默认规则排好的新列表,不会改动原名单;members.sort() 则原地重排并返回 None。本项目要保留报名先后,所以把排序结果存入另一个变量,只用来输出。

sorted_vs_sort.py
members = ["王宁", "李悦", "陈晨"]
display_members = sorted(members)
print("展示:", display_members)
print("原顺序:", members)
print("原地排序返回:", members.sort())
print("原地排序后:", members)
运行结果
展示: ['李悦', '王宁', '陈晨']
原顺序: ['王宁', '李悦', '陈晨']
原地排序返回: None
原地排序后: ['李悦', '王宁', '陈晨']

这段代码最后两行是对照实验,基础程序不调用 members.sort()。默认字符串排序依据字符编码,不等同于中文姓名的拼音顺序;实际顺序要以运行结果为准,不把它宣称为“按姓名拼音排序”。

6.7 完成列表版报名名单管理器

基础程序分四段:从空列表反复录入,q 或 Q 只结束录入;之后各做一次取消和查询;最后从名单本身统计人数并展示原顺序与排序结果。这里还不是第 8 周的完整菜单,所以取消、查询不会反复出现。

member_list.py
# 第6章:列表版报名名单管理器
# 教学简化:姓名暂作唯一标识;第7章再用学号区分同名学生。
members = []

while True:
    student_name = input("新增姓名(q结束录入):").strip()
    if student_name.lower() == "q":
        break
    if student_name == "":
        print("新增失败:姓名不能为空")
    elif student_name in members:
        print("新增失败:姓名已存在")
    else:
        members.append(student_name)
        print(f"新增成功:{student_name}")

cancel_name = input("取消姓名(直接回车跳过):").strip()
if cancel_name == "":
    print("跳过取消")
elif cancel_name in members:
    members.remove(cancel_name)
    print(f"取消成功:{cancel_name}")
else:
    print("取消失败:姓名不存在")

query_name = input("查询姓名(直接回车跳过):").strip()
if query_name == "":
    print("跳过查询")
elif query_name in members:
    print(f"查询结果:{query_name}已报名")
else:
    print(f"查询结果:{query_name}未报名")

print(f"当前人数:{len(members)}")
print(f"原报名顺序:{members}")
print("排序展示:")
if len(members) == 0:
    print("暂无成员")
else:
    display_members = sorted(members)
    for index in range(len(display_members)):
        print(f"{index + 1}. {display_members[index]}")
print(f"展示后原名单:{members}")

示例输入依次为“王宁、陈晨、李悦、q、陈晨、李悦”。应得到:陈晨取消成功,李悦已报名,当前人数 2;原顺序为王宁、李悦,排序展示为李悦、王宁,展示后原名单不变。运行需要按提示依次输入;“在线运行”会复制当前代码并打开指定的 W3Cschool 新标签页,需自行粘贴和输入数据。

  1. 1
    用一个真实名单承载状态

    姓名只存在 members,人数由 len(members) 即时计算。

  2. 2
    失败操作不改变状态

    空姓名、重复姓名、不存在的取消对象,只报告原因,不动列表。

  3. 3
    查询只读

    查询前后人数和顺序应完全一样;排序展示也不改原名单。

6.8 八组状态测试与错误诊所

测试不要只看最后人数,还要检查具体姓名和顺序。表中箭头表示先后输入;“空”表示在取消或查询提示处直接按回车。每组都从重新运行程序开始,不能沿用上一组内存。

组录入 → 取消 → 查询应看到的关键证据
A王宁、陈晨、李悦、q → 陈晨 → 李悦2 人;原序王宁、李悦;查询已报名
B“ 陈晨 ”、陈晨、q → 空 → 陈晨清洗后重复被拒;1 人
Cq → 空 → 陈晨0 人;暂无成员;查询未报名
D陈晨、q → 王宁 → 王宁取消失败;原名单仍含陈晨
E空白姓名、“ Q ” → 空 → 空空姓名失败;大写 Q 结束;0 人
F陈晨、q → 陈晨 → 陈晨取消后 0 人;查询未报名
G王宁、李悦、陈晨、q → 空 → 空原序与排序展示不同;展示后原序不变
H至少五个不同姓名、q → 空 → 空人数与实际名单一致,编号从 1 开始

若空表访问时出现 IndexError,找未经检查的索引;取消不存在的姓名出现 ValueError,找是否跳过了成员判断;名单突然变成 None,找是否把 append() 或 remove() 的返回值赋回变量;排序后原名单变了,找是否误用了 sort()。先复现最短输入,再修正并重跑原失败组。

6.9 练习与个人任务

先预测,再揭示

  1. 索引和人数

    members = ["陈晨", "王宁"] 时,members[1] 与 len(members) 是什么?

    查看答案

    分别是王宁与 2。索引从 0 开始,长度是元素总数。

  2. 重复输入

    清洗后 name 已在名单中,再执行新增分支,人数应怎样变化?

    查看答案

    不变。重复检查应挡在 append() 之前。

  3. 新结果还是原地修改

    执行 display = sorted(members) 后,members 的顺序会变吗?

    查看答案

    不会;display 是新列表。members.sort() 才原地改变。

第 6 周 · 个人机房任务

独立完成名单管理器

每位学生独立编写、运行和解释自己的程序;同伴临时配对只做黑盒测试,不合并源文件。页面示例帮助理解操作顺序,个人提交必须有自己的测试证据。

最低验收要求

  • 提交 w06_学号_member_list.py:从空名单开始,录入至 q,随后一次取消、一次查询和最终展示。
  • 空姓名、重复姓名、不存在成员的取消都要有明确提示;查询不改变名单。
  • 提交 A—H 八组“预期—实际—结论—修正后复测”,至少包含五位不同姓名、空表、重复输入。
  • 限时个人修改另存文件:容量改为 3。新增时先判空、再判重复、最后判满;满额不结束录入,还能继续读到 q。
  • 提交同伴互测单与 80—150 字复盘,能解释原顺序和排序展示为何不同。
下载完整演示代码

本章不引入嵌套列表、列表推导式、字典、自定义函数或文件保存。程序退出后,内存中的名单不会自动留存;同名冲突也尚未解决。这些是明确的阶段边界,不是通过假数据掩盖的问题。

章末检查

  • 我能说出元素值、索引、展示编号的区别。
  • 我会在 remove() 前检查成员存在。
  • 我不用方法返回值覆盖原列表。
  • 人数来自当前名单,查询和排序展示不改变原顺序。

延伸阅读:廖雪峰《Python教程》在线 PDF中的“list 和 tuple”,以及菜鸟教程 Python 3 列表。本章实例与验收规则按 ClubHub 教学项目重新组织。