从零到接单 04:列表、元组、字典、集合——数据的四种容器
系列目录:本文是「从零到接单:Python 自动化与爬虫实战」系列的第 4 篇。上一篇我们学会了条件和循环,程序能做判断和重复执行了。这篇进入 Python 的"瑞士军刀"——四种数据结构。爬虫抓回来的数据怎么存、怎么查、怎么改?全靠它们。
变量只能存一个值,但实际工作中要处理的是一批数据。比如:
- 爬虫抓下来的 1000 条新闻标题
- Excel 表格里 500 行员工信息
- 去重后的 200 个客户邮箱
Python 提供了四种"容器"来装这些数据:
| 类型 | 写法 | 特点 | 场景 |
|------|------|------|------|
| 列表 list | [1, 2, 3] | 有序、可改、可重复 | 存储一系列同类数据 |
| 元组 tuple | (1, 2, 3) | 有序、不可改、可重复 | 存储不会变的数据(如坐标) |
| 字典 dict | {"a": 1} | 键值对、可改、键唯一 | 存储有"名字"的数据(如配置) |
| 集合 set | {1, 2, 3} | 无序、不可重复 | 去重、集合运算 |
一、列表(list):最常用的容器
创建列表
# 空列表
empty = []
# 不同类型的元素(实际中建议同一类型)
numbers = [1, 2, 3, 4, 5]
fruits = ["苹果", "香蕉", "橘子"]
mixed = [1, "hello", True, 3.14]
访问元素(索引)
fruits = ["苹果", "香蕉", "橘子", "葡萄"]
print(fruits[0]) # 苹果(索引从 0 开始!)
print(fruits[1]) # 香蕉
print(fruits[-1]) # 葡萄(负数从末尾倒数)
print(fruits[-2]) # 橘子
# 切片:取一段
print(fruits[1:3]) # ['香蕉', '橘子'](不包含索引 3)
print(fruits[:2]) # ['苹果', '香蕉'](从头到索引 2)
print(fruits[2:]) # ['橘子', '葡萄'](从索引 2 到末尾)
print(fruits[::2]) # ['苹果', '橘子'](每 2 个取 1 个)
常用操作
fruits = ["苹果", "香蕉"]
# 添加
fruits.append("橘子") # 末尾添加:['苹果', '香蕉', '橘子']
fruits.insert(1, "葡萄") # 指定位置插入:['苹果', '葡萄', '香蕉', '橘子']
# 删除
fruits.remove("香蕉") # 按值删除:['苹果', '葡萄', '橘子']
popped = fruits.pop() # 弹出最后一个:popped='橘子'
popped2 = fruits.pop(0) # 弹出索引 0:popped2='苹果'
# 删除指定索引
del fruits[0] # 删除索引 0 的元素
# 查找
fruits = ["苹果", "香蕉", "橘子", "苹果"]
print(fruits.index("香蕉")) # 1(第一次出现的位置)
print(fruits.count("苹果")) # 2(出现次数)
# 排序
numbers = [3, 1, 4, 1, 5, 9, 2]
numbers.sort() # 原地排序:[1, 1, 2, 3, 4, 5, 9]
numbers.sort(reverse=True) # 降序:[9, 5, 4, 3, 2, 1, 1]
sorted_numbers = sorted(numbers) # 返回新列表,原列表不变
# 反转
fruits.reverse() # 原地反转
# 其他
print(len(fruits)) # 长度
print("苹果" in fruits) # 是否包含 → True
列表推导式(超实用!)
# 生成 1 到 10 的平方
squares = [x ** 2 for x in range(1, 11)]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 带条件的列表推导式
even_squares = [x ** 2 for x in range(1, 11) if x % 2 == 0]
print(even_squares) # [4, 16, 36, 64, 100]
# 爬虫中常见:提取所有链接的文本
urls = ["https://a.com/1", "https://b.com/2", "https://c.com/3"]
paths = [url.split("/")[-1] for url in urls]
print(paths) # ['1', '2', '3']
二、元组(tuple):不可变的列表
# 创建
point = (3, 4)
rgb = (255, 128, 0)
single = (1,) # 只有一个元素时必须加逗号!
empty = () # 空元组
# 访问(和列表一样)
print(point[0]) # 3
print(point[1]) # 4
# ❌ 不能修改
# point[0] = 5 # TypeError! 元组不可修改
# 解包(unpacking)
x, y = point
print(x, y) # 3 4
a, b, c = (1, 2, 3)
print(a, b, c) # 1 2 3
# 交换变量(Python 独有技巧)
a = 10
b = 20
a, b = b, a
print(a, b) # 20 10
什么时候用元组? 当你确定这组数据不会改变时。比如函数返回多个值时、作为字典的键时。
三、字典(dict):有名字的容器
字典存储键值对,通过"键"来找"值"。这是 Python 中最强大的数据结构之一。
创建和访问
# 创建
person = {
"name": "小明",
"age": 25,
"city": "北京",
"skills": ["Python", "爬虫", "Excel"]
}
# 访问
print(person["name"]) # 小明
print(person.get("name")) # 小明(推荐:key 不存在返回 None 而不是报错)
print(person.get("phone")) # None(不会报错)
print(person.get("phone", "未知")) # 未知(设置默认值)
增删改查
person = {"name": "小明", "age": 25}
# 添加/修改
person["city"] = "上海" # 添加新键
person["age"] = 26 # 修改已有键
# 删除
del person["city"] # 删除键值对
phone = person.pop("phone", "无") # 安全删除,返回默认值
# 检查 key 是否存在
if "age" in person:
print(f"年龄:{person['age']}")
# 遍历
for key in person:
print(f"{key}: {person[key]}")
# 同时拿到 key 和 value(推荐)
for key, value in person.items():
print(f"{key}: {value}")
# 只拿 keys 或 values
print(list(person.keys())) # ['name', 'age']
print(list(person.values())) # ['小明', 26]
字典推导式
# 生成平方字典
squares = {x: x**2 for x in range(1, 6)}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 爬虫常见:把列表转成字典方便查询
names = ["张三", "李四", "王五"]
scores = [85, 92, 78]
score_dict = {name: score for name, score in zip(names, scores)}
print(score_dict) # {'张三': 85, '李四': 92, '王五': 78}
嵌套结构(处理真实数据的关键)
# JSON 数据的典型结构
users = [
{"name": "张三", "age": 25, "email": "zhangsan@example.com"},
{"name": "李四", "age": 30, "email": "lisi@example.com"},
{"name": "王五", "age": 28, "email": "wangwu@example.com"},
]
# 找到所有成年用户的名字
adults = [user["name"] for user in users if user["age"] >= 18]
print(adults) # ['张三', '李四', '王五']
四、集合(set):不重复的容器
# 创建
fruits = {"苹果", "香蕉", "橘子"}
numbers = set([1, 2, 3, 2, 1]) # 从列表创建,自动去重
print(numbers) # {1, 2, 3}
empty = set() # 空集合(不能用 {},那是空字典!)
常见操作
s = {1, 2, 3}
# 增删
s.add(4) # {1, 2, 3, 4}
s.remove(2) # {1, 3, 4}(元素不存在会报错)
s.discard(99) # 安全删除,不存在不报错
# 集合运算
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # 并集:{1, 2, 3, 4, 5, 6}
print(a & b) # 交集:{3, 4}
print(a - b) # 差集(a 有 b 没有):{1, 2}
print(a ^ b) # 对称差(只在一边的):{1, 2, 5, 6}
爬虫中最常见的用法:去重
# 抓到的 URL 列表,可能有重复
urls = [
"https://a.com/1",
"https://a.com/2",
"https://a.com/1", # 重复
"https://a.com/3",
"https://a.com/2", # 重复
]
unique_urls = list(set(urls))
print(unique_urls)
print(f"去重前:{len(urls)} 条,去重后:{len(unique_urls)} 条")
五、实战练习
练习 1:学生成绩管理系统
students = []
while True:
print("\n=== 学生成绩管理 ===")
print("1. 添加学生")
print("2. 查看所有学生")
print("3. 按姓名查找")
print("4. 统计平均分")
print("5. 退出")
choice = input("请选择:")
if choice == "1":
name = input("学生姓名:")
score = float(input("成绩:"))
students.append({"name": name, "score": score})
print(f"已添加 {name}")
elif choice == "2":
if not students:
print("暂无学生数据")
else:
for i, stu in enumerate(students, 1):
print(f"{i}. {stu['name']} - {stu['score']}分")
elif choice == "3":
name = input("输入要查找的姓名:")
found = [s for s in students if s["name"] == name]
if found:
for s in found:
print(f"{s['name']}: {s['score']}分")
else:
print("未找到")
elif choice == "4":
if students:
avg = sum(s["score"] for s in students) / len(students)
print(f"平均分:{avg:.1f}")
else:
print("暂无数据")
elif choice == "5":
print("再见!")
break
总结
| 结构 | 创建 | 有序 | 可变 | 可重复 | 主力场景 |
|------|------|------|------|--------|----------|
| list | [...] | ✅ | ✅ | ✅ | 存储序列数据 |
| tuple | (...) | ✅ | ❌ | ✅ | 不可变数据 |
| dict | {k:v} | ✅* | ✅ | 键不重复 | 键值映射 |
| set | {...} | ❌ | ✅ | ❌ | 去重、集合运算 |
* Python 3.7+ 字典保序。
四种数据结构会在后面的爬虫和自动化实战中反复用到。下一篇,我们将学习函数与模块——把代码组织成可复用的"零件"。
练习:写一个"通讯录"程序,用字典存储(姓名→电话号码)。支持添加联系人、查找联系人、显示全部联系人和删除联系人。
Comments
Sign in to leave a comment.