从零到接单 04:列表、元组、字典、集合——数据的四种容器

pythontutorialbeginner自动化爬虫接单数据结构

系列目录:本文是「从零到接单: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.