首页/Python 与数据实践/序列、字典与集合
Python 与数据实践 · 第 03 章

序列、字典与集合

前面学过的变量,一次只能攥住一个值。可现实里的数据很少这么"独来独往":一张订单挂着好几件商品,一张名片挤着姓名、电话、职位好几个字段,一个商品可能贴着一堆标签。Python 为这类"批量数据"准备了一整套内置的打包工具…

约 38 分钟15,101 字30 个代码示例

一个变量,装下一组数据

前面学过的变量,一次只能攥住一个值。可现实里的数据很少这么"独来独往":一张订单挂着好几件商品,一张名片挤着姓名、电话、职位好几个字段,一个商品可能贴着一堆标签。Python 为这类"批量数据"准备了一整套内置的打包工具,统称容器。

先来一张全家福:

类型 俗称 能否原地修改 是否有序 元素可否重复 示例
list 列表 能 有序 可重复 [1, 2, 2, 3]
tuple 元组 不能 有序 可重复 (1, 2, 3)
dict 字典 能 有序(3.7 版本起保证) 键不可重复 {"a": 1, "b": 2}
set 集合 能 无序 不可重复 {1, 2, 3}
str 字符串 不能 有序 可重复 "hello"

顺带说一句:字符串其实也可以当成"一串排好队的字符"来用,本章的不少操作对它照样有效,讲解过程中会穿插演示。


列表:随改随用的活页本

列表大概是日后出场率最高的容器——它有顺序、能随时往里加东西、删东西、改东西,装什么类型的元素都行。

怎么造一个列表

# 直接写出字面量
orders = [301, 302, 303, 304, 305]
mixed_bag = [42, "少冰", 3.5, True, None]
blank = []  # 空列表,先备着,回头往里装

# 用 list() 从别的数据转换而来
letters = list("abc")        # ['a', 'b', 'c']
from_tuple = list((7, 8, 9))  # [7, 8, 9]

按位置取值:索引与切片

menu = ["美式", "拿铁", "卡布奇诺", "摩卡", "冷萃"]

# 索引从 0 开始数
print(menu[0])   # 美式
print(menu[-1])  # 冷萃(负数表示从队尾倒着数)

# 切片格式 [起点:终点:步长],特点是"包头不包尾"
print(menu[1:4])    # ['拿铁', '卡布奇诺', '摩卡'] —— 落在区间 [1, 4) 内的元素
print(menu[:3])     # ['美式', '拿铁', '卡布奇诺'] —— 起点不写就从头开始
print(menu[2:])     # ['卡布奇诺', '摩卡', '冷萃'] —— 终点不写就取到队尾
print(menu[::2])    # ['美式', '卡布奇诺', '冷萃'] —— 步长 2,隔一个取一个
print(menu[1:4:2])  # ['拿铁', '摩卡'] —— 区间 [1, 4),步长 2
print(menu[::-1])   # ['冷萃', '摩卡', '卡布奇诺', '拿铁', '美式'] —— 步长为负,整串倒着走

增删改查一把抓

queue = [11, 22, 33]

# 往里加
queue.append(44)        # [11, 22, 33, 44]      追加到队尾
queue.insert(0, 5)      # [5, 11, 22, 33, 44]   插队到指定位置
queue.extend([55, 66])  # [5, 11, 22, 33, 44, 55, 66]  把另一份列表整个接上来

# 往外删
queue.remove(5)      # [11, 22, 33, 44, 55, 66]  按值删,只删碰到的第一个
tail = queue.pop()   # 66,队列变为 [11, 22, 33, 44, 55]  弹出队尾并返回
head = queue.pop(0)  # 11,队列变为 [22, 33, 44, 55]      弹出指定位置并返回
del queue[0]         # [33, 44, 55]  按位置删
queue.clear()        # []             一键清空

# 查位置、数次数
votes = [10, 20, 30, 20, 40]
print(votes.index(20))  # 1  第一次出现的位置
print(votes.count(20))  # 2  总共出现了几回

# 排序与翻转(会直接改动原列表)
fares = [23, 8, 42, 8, 15]
fares.sort()              # [8, 8, 15, 23, 42]  从小到大,原地排
fares.sort(reverse=True)  # [42, 23, 15, 8, 8]  从大到小
fares.reverse()           # [8, 8, 15, 23, 42]  原地翻转

# 想保留原始顺序?改用 sorted(),它返回新列表
fares = [23, 8, 42]
ordered = sorted(fares)   # [8, 23, 42],fares 本身原封不动

# 修改指定位置
fares[0] = 99   # [99, 8, 42]
# fares[3] = 1  # IndexError: list assignment index out of range

元组:一次定稿、终身只读

元组像是"塑封"过的列表:内容一旦写好就不许再动。适合存放那些不该被意外修改的数据,比如坐标、配置项。

怎么造一个元组

# 字面量写法,圆括号可以偷懒不写
pixel = (120, 200, 60)
pixel = 120, 200, 60    # 和上面完全等价
single = (9,)           # 只有一个元素时,逗号不能省!写成 (9) 只是个普通数字
empty = ()              # 空元组

# 用 tuple() 转换
t = tuple([4, 5, 6])    # (4, 5, 6)
t = tuple("hi")         # ('h', 'i')

能做什么、不能做什么

t = (6, 3, 9, 3, 12)

# 索引和切片,玩法与列表完全一致
print(t[0])    # 6
print(t[-1])   # 12
print(t[1:4])  # (3, 9, 3)

# 查询没问题
print(t.index(3))  # 1
print(t.count(3))  # 2
print(9 in t)      # True

# 但改动是禁区
# t[0] = 66   # TypeError: 'tuple' object does not support item assignment
# t.append(7) # AttributeError

解包:一口气把值分给多个变量

# 基础解包
rgb = (52, 96, 235)
r, g, b = rgb
print(r, g, b)  # 52 96 235

# 星号解包:剩下的统统归它
head, *tail = (1, 2, 3, 4)
print(head)  # 1
print(tail)  # [2, 3, 4]

*front, tail = (1, 2, 3, 4)
print(front)  # [1, 2, 3]
print(tail)   # 4

# 交换两个变量的经典写法
left, right = "前", "后"
left, right = right, left  # 现在 left="后",right="前"

字典:拿"名字"去查数据的登记簿

字典不靠位置找数据,而是靠键(key)直达对应的值(value),查起来非常快。

怎么造一个字典

# 字面量写法
book = {
    "title": "三体",
    "author": "刘慈欣",
    "year": 2008
}
empty = {}  # 空字典

# 用 dict() 创建
book = dict(title="球状闪电", author="刘慈欣", year=2004)
book = dict([("title", "三体"), ("year", 2008)])

# 拿一串键批量开空档,值先统一填充
shifts = dict.fromkeys(["早班", "中班", "晚班"], 0)
# {'早班': 0, '中班': 0, '晚班': 0}

读与写

book = {"title": "三体", "author": "刘慈欣"}

# 读取:方括号直取
print(book["title"])     # 三体
# print(book["price"])   # KeyError!键不存在就直接翻脸

# 稳妥的读法:get,查不到也不炸
print(book.get("price"))          # None(不传默认值时)
print(book.get("price", "未登记"))  # 未登记(给个兜底值)

# 新增 / 修改
book["price"] = 45.5   # 原来没有这个键 → 新增
book["year"] = 2008    # 已有这个键 → 覆盖旧值

# 批量写入
book.update({"publisher": "重庆出版社", "year": 2008})

# 删除
del book["publisher"]        # 按键删
value = book.pop("price")    # 删掉并顺手把值拿回来
last_item = book.popitem()   # 弹出最后写进来的那对键值(3.7 版本起)
book.clear()                 # 清空

两条铁律

# 铁律一:键必须是不可变(可哈希)的类型
workable = {
    "level": 3,       # str ✓
    1024: "内存",     # int ✓
    (2, 5): "坐标",   # tuple ✓
    # [1, 2]: " Nope",  # list ✗ 列表可变,没法当键!
}

# 铁律二:键天然去重,后来者覆盖先行者
floor = {"楼层": 1, "楼层": 3}
print(floor)  # {'楼层': 3}

集合:自动去重的百宝袋

集合有两大人设:元素不重复、不讲究顺序。它还是做数学集合运算的一把好手。

怎么造一个集合

# 字面量写法,重复的元素会被自动吞掉
labels = {"新品", "热销", "新品", "热销"}  # {'新品', '热销'}
empty = set()  # 注意:空集合必须用 set(),写成 {} 造出来的是空字典!

# 用 set() 转换
nums = set([1, 2, 2, 3])  # {1, 2, 3}
chars = set("moon")       # {'m', 'o', 'n'}

集合的日常操作

s = {1, 2, 3}

# 添加与删除
s.add(4)        # {1, 2, 3, 4}
s.remove(2)     # {1, 3, 4} —— 要删的元素不存在会抛 KeyError
s.discard(10)   # 删不存在的元素也一声不吭,不报错
s.pop()         # 随机弹出一个元素并返回(谁先谁后说不准)
s.clear()       # set()

# 去重是集合的主场
orders = [501, 502, 501, 503, 502, 502]
unique = list(set(orders))  # [501, 502, 503](顺序不保证)

五类容器通吃的工具函数

下面这批内置函数,list、tuple、dict、set、str 全部适用:

# len() —— 数一数有多少个元素
len([1, 2, 3])         # 3
len((7, 8, 9))         # 3
len({"冷萃": 12, "拿铁": 8})  # 2(按键值对计数)
len("cake")            # 4

# max() / min() —— 挑最大、挑最小
max([23, 8, 42, 15])   # 42
min((6, 1, 8))         # 1
max("cake")            # 'k'(按字符编码比较)

# sum() —— 求和(元素得是数字)
sum([2, 4, 6, 8])      # 20
sum((1, 2, 3))         # 6

# sorted() —— 排序,一律返回新列表
sorted([3, 1, 2])               # [1, 2, 3]
sorted((9, 5, 7))               # [5, 7, 9] —— 元组进来,列表出去
sorted("onion")                 # ['i', 'n', 'n', 'o', 'o']
sorted([3, 1, 2], reverse=True)  # [3, 2, 1]

# reversed() —— 反转,返回的是迭代器,套一层 list() 再看
list(reversed([1, 2, 3]))  # [3, 2, 1]
list(reversed("abc"))      # ['c', 'b', 'a']

容器身边的运算符

判断"在不在":in / not in

# 列表
42 in [23, 42, 8]      # True
9 not in [23, 42, 8]   # True

# 元组
"春" in ("春", "夏", "秋")  # True

# 字符串 —— 查的是子串
"ca" in "cake"      # True
"z" not in "cake"   # True

# 字典 —— 查的是键,跟值没关系
"冷萃" in {"冷萃": 12, "拿铁": 8}   # True
12 in {"冷萃": 12, "拿铁": 8}      # False

# 集合
2 in {1, 2, 3}       # True

拼接与复制:+ 与 *

# + 拼接(只有有序容器支持)
[1, 2] + [3, 4]      # [1, 2, 3, 4]
(1, 2) + (3, 4)      # (1, 2, 3, 4)
"hot" + "dog"        # "hotdog"
# {1, 2} + {3, 4}    # TypeError!集合不吃这一套

# * 复制(同样只限有序容器)
[0, 1] * 3           # [0, 1, 0, 1, 0, 1]
"~" * 8              # "~~~~~~~~"

比内容:== / !=

== 关心的是两个容器装的东西是否一样,而不是它们是不是同一个对象。

判定的四步逻辑:

  1. 类型先对上:类型不同,哪怕内容看着一样也不算相等,[1, 2] == (1, 2) 是 False。
  2. 长度得一样:长度对不上,直接判不等。
  3. 逐个位置比:从第 0 位开始一对一对地比;碰到嵌套容器,会一层层递归往里比。
  4. 元素自己说了算:容器不掺和元素怎么算相等,而是调用元素自己的比较逻辑,所以 [1, 2] == [1.0, 2.0] 是 True。
# 基本比较
[2, 4, 6] == [2, 4, 6]    # True,内容一致
[2, 4, 6] == [6, 4, 2]    # False,顺序也是内容的一部分

# 类型不同
[1, 2] == (1, 2)          # False

# 嵌套结构递归比较
[[1, 2], [3]] == [[1, 2], [3]]  # True

# 字典只看键值对是否一致
{"a": 1, "b": 2} == {"b": 2, "a": 1}  # True,书写顺序不影响

提醒:== 判断的是值相等,不是身份相同(内存里是不是同一个对象)。想知道身份,得用下面的 is。

比身份:is / is not

is 回答的问题是:两个变量是不是指着内存里同一个对象?等价于比较它们的 id()。

a = [1, 2, 3]
b = a          # b 和 a 指向同一个列表
c = [1, 2, 3]  # c 另起炉灶,新建了一个列表

a is b        # True,确实是同一个对象
a is c        # False,内容虽同,对象是两个
a == c        # True,光看内容是相等的
a is not c    # True

高频使用场景:

# 判断是否为 None —— 官方推荐写法
value = None
if value is None:
    print("空值")

# 判断单例对象
flag = True
flag is True  # True

# 小整数缓存:解释器的优化手段
a = 100
b = 100
a is b        # True(-5 到 256 范围内的整数会被缓存共用)

x = 1000
y = 1000
x is y        # False(通常如此,具体取决于解释器实现)

一句话分清: - == 问"你们俩长得一样吗?"(比值) - is 问"你们俩是同一个人吗?"(比身份) - 跟 None 比较时,永远用 is,别用 ==

集合专属:&、|、-、^

这四个位运算符放在集合身上,含义摇身一变成了集合运算:

vip_old = {101, 102, 103}
vip_new = {102, 103, 104}

# & 交集 —— 两边都有的
vip_old & vip_new   # {102, 103}

# | 并集 —— 两边合起来,自动去重
vip_old | vip_new   # {101, 102, 103, 104}

# - 差集 —— 我有而你没有的
vip_old - vip_new   # {101}
vip_new - vip_old   # {104}

# ^ 对称差集 —— 只属于其中一边的
vip_old ^ vip_new   # {101, 104}

# ~ 是整数按位取反用的,跟集合没关系

两种写法对照:运算符 vs 方法名

vip_old = {101, 102, 103}
vip_new = {102, 103, 104}

# 运算符风格
vip_old & vip_new   # {102, 103}
vip_old | vip_new   # {101, 102, 103, 104}
vip_old - vip_new   # {101}
vip_old ^ vip_new   # {101, 104}

# 方法风格(效果一样)
vip_old.intersection(vip_new)          # {102, 103}
vip_old.union(vip_new)                 # {101, 102, 103, 104}
vip_old.difference(vip_new)            # {101}
vip_old.symmetric_difference(vip_new)  # {101, 104}

注意:这四个符号只对集合(set)生效,套在 list、tuple、dict 上会直接报错。


遍历容器的几种姿势

最朴素的 for

# 列表
for item in [10, 20, 30]:
    print(item)

# 元组
for season in ("春", "夏", "秋"):
    print(season)

# 字符串
for ch in "tea":
    print(ch)  # t, e, a

字典的三种遍历角度

cafe = {"冷萃": 18, "拿铁": 22, "抹茶": 25}

# 默认拿到的是键
for name in cafe:
    print(name)

# 键值成对拿
for name, price in cafe.items():
    print(name, price)

# 只看值
for price in cafe.values():
    print(price)

# 只看键(写明白一点)
for name in cafe.keys():
    print(name)

集合也能遍历,但别指望顺序

tags = {"科幻", "悬疑", "剧情"}
for tag in tags:
    print(tag)
# 集合内部无序,每次遍历输出的先后不一定相同

enumerate():序号和数据一起发

光遍历不够,还想知道"这是第几个",就请 enumerate() 出场:

steps = ["取号", "等待", "办理"]

# 基本用法
for index, step in enumerate(steps):
    print(f"{index}: {step}")
# 0: 取号
# 1: 等待
# 2: 办理

# 让编号从 1 开始
for no, step in enumerate(steps, start=1):
    print(f"{no}. {step}")
# 1. 取号
# 2. 等待
# 3. 办理

zip():多条队伍并排走

把多个容器按位置两两配对,同步遍历:

lines = ["1号线", "2号线", "3号线"]
loads = [850, 620, 730]

for line, load in zip(lines, loads):
    print(f"{line}今日客流 {load} 万人次")

# zip 以最短的那条为准,长了的部分直接舍弃
short = [10, 20]
long = ["a", "b", "c"]
for x, y in zip(short, long):
    print(x, y)
# 只会输出两组:10 a 和 20 b

练习

练习一:副本与本尊(列表 + 身份运算符)

逐行阅读下面的代码,先在纸上写出每个 print 的输出,再运行验证。

points = [6, 1, 8, 1, 4]
mirror = points[:]
print(points == mirror, points is mirror)
points[0] = 10

print(points[0], mirror[0])
print(points == mirror, points is mirror)
print(len(points))

i = 0
hits = 0
while i < len(points):
    if points[i] > 3:
        hits += 1
    i += 1
print(hits)

练习二:元组、集合与成员判断

逐行阅读下面的代码,先在纸上写出每个 print 的输出,再运行验证。

t = (4, 7, 1, 7)
head, *tail = t
print(head, tail)
print(sorted(t))
print(7 in t, 3 not in t)

s = set(t)
print(len(s))
s2 = {7, 1, 9}
print(s & s2, s | s2)

练习三:字典的增删与遍历

逐行阅读下面的代码,先在纸上写出每个 print 的输出,再运行验证。

menu = {"面条": 12, "米饭": 22}
menu["饺子"] = 30
menu.update({"面条": 15})
print(len(menu))
print("面条" in menu, 22 in menu)
print(menu.get("汉堡", 0))

keys = []
for k in menu:
    keys.append(k)
print(keys)

i = 0
while i < len(keys):
    k = keys[i]
    if menu[k] > 15:
        print(k)
    i += 1

练习四:字符串与循环

逐行阅读下面的代码,先在纸上写出每个 print 的输出,再运行验证。

word = "banana"
print(word[1:4])
print("na" in word, "q" not in word)
print(word + " juice", word * 2)

i = 0
vowels = "aeiou"
count = 0
while i < len(word):
    if word[i] in vowels:
        count += 1
    i += 1
print(count)
print(f"length: {len(word)}")

练习五:zip 与 enumerate 联合作业

逐行阅读下面的代码,先在纸上写出每个 print 的输出,再运行验证。

dishes = ["宫保鸡丁", "麻婆豆腐"]
prices = (38, 26)

pairs = []
for dish, price in zip(dishes, prices):
    pairs.append(f"{dish}-{price}")
print(pairs)

for no, dish in enumerate(dishes, 1):
    print(no, dish)

d = {}
i = 0
while i < len(dishes):
    if prices[i] > 20:
        d[dishes[i]] = prices[i]
    i += 1
print(len(d))
print(d.get("宫保鸡丁"))
print(d.get("回锅肉", "今日无货"))

练习六:员工档案综合实战

下面是一份员工信息列表,请按任务要求逐一编写代码。

要求: 全部任务只用循环和条件判断实现,先不要使用列表推导式。

staff = [
    {"id": 100237, "name": "周建军", "sex": "男", "age": 35, "city": "四川省 成都市", "phone": "13801234567"},
    {"id": 205481, "name": "吴丽华", "sex": "女", "age": 28, "city": "浙江省 杭州市", "phone": "15923456789"},
    {"id": 309712, "name": "孙志强", "sex": "男", "age": 42, "city": "山东省 济南市", "phone": "18634567890"},
    {"id": 415836, "name": "李雪梅", "sex": "女", "age": 31, "city": "江苏省 南京市", "phone": "17745678901"},
    {"id": 520318, "name": "陈国栋", "sex": "男", "age": 38, "city": "广东省 广州市", "phone": "13556789012"},
    {"id": 601942, "name": "王秀兰", "sex": "女", "age": 44, "city": "河南省 郑州市", "phone": "15867890123"},
    {"id": 714728, "name": "周敏", "sex": "女", "age": 26, "city": "湖北省 武汉市", "phone": "13678901234"},
    {"id": 826503, "name": "郑涛", "sex": "男", "age": 29, "city": "福建省 福州市", "phone": "18890123456"},
    {"id": 937215, "name": "何静", "sex": "女", "age": 33, "city": "湖南省 长沙市", "phone": "15601237893"},
    {"id": 104836, "name": "罗建国", "sex": "男", "age": 45, "city": "陕西省 西安市", "phone": "13912345678"},
    {"id": 215940, "name": "高芳", "sex": "女", "age": 24, "city": "四川省 绵阳市", "phone": "15023456781"},
    {"id": 327164, "name": "林伟", "sex": "男", "age": 36, "city": "安徽省 合肥市", "phone": "18734567892"},
    {"id": 438579, "name": "黄丽", "sex": "女", "age": 27, "city": "江西省 南昌市", "phone": "13645678933"},
    {"id": 541690, "name": "张勇", "sex": "男", "age": 41, "city": "辽宁省 沈阳市", "phone": "15956789012"},
    {"id": 652873, "name": "徐桂英", "sex": "女", "age": 43, "city": "黑龙江省 哈尔滨市", "phone": "13867890123"},
    {"id": 763915, "name": "周涛", "sex": "男", "age": 30, "city": "河北省 石家庄市", "phone": "18678901273"},
    {"id": 874620, "name": "谢婷", "sex": "女", "age": 22, "city": "云南省 昆明市", "phone": "15789012346"},
    {"id": 985134, "name": "马超", "sex": "男", "age": 34, "city": "贵州省 贵阳市", "phone": "13501234568"},
    {"id": 109257, "name": "唐秀英", "sex": "女", "age": 39, "city": "广西壮族自治区 南宁市", "phone": "15812345673"},
    {"id": 210348, "name": "冯军", "sex": "男", "age": 25, "city": "山西省 太原市", "phone": "18623456789"},
    {"id": 321569, "name": "曹丽华", "sex": "女", "age": 37, "city": "吉林省 长春市", "phone": "13734567890"},
    {"id": 432680, "name": "邓强", "sex": "男", "age": 28, "city": "重庆市 重庆市", "phone": "15945678901"},
    {"id": 543791, "name": "萧芳", "sex": "女", "age": 26, "city": "甘肃省 兰州市", "phone": "15856789023"},
    {"id": 654802, "name": "董伟", "sex": "男", "age": 33, "city": "青海省 西宁市", "phone": "13567890124"},
    {"id": 765913, "name": "程娜", "sex": "女", "age": 29, "city": "海南省 海口市", "phone": "18678901235"},
    {"id": 876024, "name": "宋磊", "sex": "男", "age": 36, "city": "内蒙古自治区 呼和浩特市", "phone": "17890123456"},
    {"id": 987135, "name": "石秀珍", "sex": "女", "age": 41, "city": "新疆维吾尔自治区 乌鲁木齐市", "phone": "13690123457"},
    {"id": 108246, "name": "姚刚", "sex": "男", "age": 27, "city": "宁夏回族自治区 银川市", "phone": "18901234563"},
    {"id": 219357, "name": "潘淑华", "sex": "女", "age": 34, "city": "西藏自治区 拉萨市", "phone": "15112345670"},
    {"id": 320468, "name": "江海", "sex": "男", "age": 39, "city": "天津市 天津市", "phone": "13623456788"},
    {"id": 431579, "name": "段鹏", "sex": "男", "age": 32, "city": "北京市 北京市", "phone": "15834567891"},
    {"id": 542680, "name": "贺晓燕", "sex": "女", "age": 23, "city": "上海市 上海市", "phone": "18745678902"},
    {"id": 653791, "name": "侯亮", "sex": "男", "age": 36, "city": "四川省 泸州市", "phone": "13556789023"},
    {"id": 764802, "name": "白春梅", "sex": "女", "age": 38, "city": "广东省 佛山市", "phone": "15667890134"},
    {"id": 875913, "name": "邹成", "sex": "男", "age": 44, "city": "湖北省 宜昌市", "phone": "17878901245"},
    {"id": 986024, "name": "龙婷", "sex": "女", "age": 25, "city": "浙江省 宁波市", "phone": "13689012356"},
    {"id": 109135, "name": "范志伟", "sex": "男", "age": 31, "city": "江苏省 苏州市", "phone": "15990123467"},
    {"id": 210246, "name": "雷小雨", "sex": "女", "age": 29, "city": "福建省 厦门市", "phone": "18701234578"},
    {"id": 321357, "name": "钟华", "sex": "男", "age": 37, "city": "山东省 青岛市", "phone": "13612345689"},
    {"id": 432468, "name": "汪桂芳", "sex": "女", "age": 42, "city": "河南省 洛阳市", "phone": "15823456790"},
    {"id": 543579, "name": "丁一鸣", "sex": "男", "age": 26, "city": "湖南省 株洲市", "phone": "18934567801"},
    {"id": 654680, "name": "阳丽", "sex": "女", "age": 33, "city": "安徽省 芜湖市", "phone": "15745678912"},
    {"id": 765791, "name": "傅国强", "sex": "男", "age": 44, "city": "江西省 九江市", "phone": "13156789023"},
    {"id": 876802, "name": "周艳", "sex": "女", "age": 27, "city": "辽宁省 大连市", "phone": "18567890134"},
    {"id": 987913, "name": "覃敏", "sex": "女", "age": 36, "city": "陕西省 宝鸡市", "phone": "15378901245"},
    {"id": 108024, "name": "龚浩", "sex": "男", "age": 23, "city": "贵州省 遵义市", "phone": "18689012356"},
    {"id": 219135, "name": "汤圆圆", "sex": "女", "age": 30, "city": "云南省 大理州", "phone": "13590123467"},
    {"id": 320246, "name": "邵伟杰", "sex": "男", "age": 35, "city": "广西壮族自治区 桂林市", "phone": "15801234578"},
    {"id": 431357, "name": "秦淑珍", "sex": "女", "age": 41, "city": "甘肃省 天水市", "phone": "13612345689"},
    {"id": 542468, "name": "曾超", "sex": "男", "age": 28, "city": "青海省 海东市", "phone": "18723456790"},
    {"id": 653579, "name": "兰海燕", "sex": "女", "age": 26, "city": "海南省 三亚市", "phone": "15534567801"},
    {"id": 764680, "name": "白俊峰", "sex": "男", "age": 39, "city": "内蒙古自治区 包头市", "phone": "13145678912"},
    {"id": 875791, "name": "温丽娟", "sex": "女", "age": 35, "city": "新疆维吾尔自治区 喀什地区", "phone": "15656789023"},
    {"id": 986802, "name": "岑龙", "sex": "男", "age": 24, "city": "宁夏回族自治区 石嘴山市", "phone": "18867890134"},
    {"id": 107913, "name": "全红梅", "sex": "女", "age": 32, "city": "西藏自治区 日喀则市", "phone": "13478901245"},
    {"id": 218024, "name": "骆天宇", "sex": "男", "age": 30, "city": "北京市 北京市", "phone": "15689012356"},
    {"id": 329135, "name": "阮诗涵", "sex": "女", "age": 22, "city": "上海市 上海市", "phone": "18790123467"},
    {"id": 430246, "name": "凌志远", "sex": "男", "age": 45, "city": "江苏省 无锡市", "phone": "13301234578"},
    {"id": 541357, "name": "覃玉婷", "sex": "女", "age": 28, "city": "浙江省 温州市", "phone": "15512345689"},
    {"id": 652468, "name": "蒲剑锋", "sex": "男", "age": 33, "city": "湖北省 襄阳市", "phone": "18623456790"},
]

任务清单:

  1. 遍历 staff 列表,用 f-string 输出每位员工的姓名和年龄,格式为 姓名: xxx, 年龄: xx

  2. 创建一个新列表 female_staff,装下所有性别为"女"的员工(即 sex 键的值为 "女")

  3. 创建一个新列表 young_female,装下所有年龄小于 30 岁的女员工

  4. 创建一个新列表 zhou_staff,装下所有姓"周"的员工(提示:name[0] 可以取出姓名的第一个字)

  5. 创建一个新列表 phone_tail_3,装下所有手机号尾号是 "3" 的员工(提示:字符串支持负索引,phone[-1] 取最后一个字符)

  6. 创建一个新列表 all_names,装下所有员工的姓名

  7. 创建一个新列表 female_names,装下所有女员工的姓名

  8. 创建一个新列表 female_contacts,其中每个元素是一个字典,只保留 name 和 phone 两个键,且只收录女员工

  9. 计算并打印全体员工的年龄总和

  10. 计算并打印全体员工的平均年龄(保留两位小数,用 f-string 格式化)

  11. 创建一个字典 result,包含两个键:"names" 对应所有员工姓名组成的列表,"ages" 对应所有员工年龄组成的列表

  12. 找到 id 为 520318 的员工,打印其姓名和手机号。如果找不到,打印 "查无此人"

  13. 判断员工中是否存在年龄大于 40 岁的男员工。如果存在,打印 True 并输出该员工的姓名;否则打印 False

  14. 判断是否所有女员工的年龄都在 40 岁以内(即没有超过 40 岁的女员工)。如果是,打印 True;否则打印 False


下一章:04 · 函数设计与 lambda