当前位置:首页>python>Python3 数据结构:选对容器,事半功倍

Python3 数据结构:选对容器,事半功倍

  • 2026-10-03 15:53:06
Python3 数据结构:选对容器,事半功倍

Python3 数据结构:选对容器,事半功倍

我是陈默,一个正拼命上岸的码农。

搬家的时候,你用什么装东西?

衣服用箱子,碗碟用泡沫盒,零碎东西用收纳袋。

不同的东西用不同的容器。编程也一样——不同的数据,用不同的数据结构。

Python 提供了四种内置容器:列表、元组、字典、集合。每一种都有自己的脾气。选错了,代码又慢又丑。选对了,事半功倍。

今天我们来做一个横向对比,帮你彻底搞清楚什么时候用什么。


1. 四种数据结构,一眼看懂

# 列表(List):有序,可改,可重复
my_list = [1, 2, 3, 2, 4]

# 元组(Tuple):有序,不可改,可重复
my_tuple = (1, 2, 3, 2, 4)

# 字典(Dict):键值对,键唯一
my_dict = {"name": "陈默", "age": 25}

# 集合(Set):无序,不可重复
my_set = {1, 2, 3, 4}

一句话总结:

  • 列表 = 排队,能加人能减人
  • 元组 = 排队,但锁死了不能动
  • 字典 = 通讯录,按名字找人
  • 集合 = 去重器,自动踢掉重复的

2. 横向对比:关键特性

特性
列表
元组
字典
集合
有序
是
是
3.7+是
否
可修改
是
否
是
是
可重复
是
是
值可以
否
语法
[](){k:v}{}
索引访问
是
是
按键
否
可做字典的键
否
是
否
否

注意: 集合虽然用 {},但空集合必须用 set(),因为 {} 是空字典。


3. 性能对比:快慢差很多

import time

data = list(range(100000))

# 列表查找:从头到尾扫一遍
start = time.time()
print(99999in data)           # True
print(f"列表查找:{time.time() - start:.4f}秒")

# 集合查找:直接定位
data_set = set(data)
start = time.time()
print(99999in data_set)       # True
print(f"集合查找:{time.time() - start:.6f}秒")

判断元素是否存在:集合是 O(1),列表是 O(n)。数据量越大,差距越大。

简单说:

  • 1万个元素里查找:集合瞬间,列表要翻半天
  • 增删元素:列表末尾快,中间慢;集合都很快
  • 排序:只有列表有 sort() 方法

4. 什么时候用列表?

能改、有顺序、允许重复。

# 待办事项
todos = ["写周报", "修 bug", "开会", "写周报"]

# 添加新任务
todos.append("学 Python")

# 完成一个
todos.pop(0)

print(todos)    # 输出: ['修 bug', '开会', '写周报', '学 Python']

典型场景:

  • 待办列表、购物车、成绩单
  • 需要排序的数据
  • 需要按位置访问的数据
  • 数据会频繁增删

5. 什么时候用元组?

不用改、保护数据、作为字典的键。

# 地理坐标(不该被修改)
beijing = (39.9, 116.4)
shanghai = (31.2, 121.5)

# 用坐标当字典的键
distance = {
    (beijing, shanghai): 1213,
    (beijing, (39.1, 117.2)): 120,
}

print(f"北京到上海:{distance[(beijing, shanghai)]}公里")
# 输出: 北京到上海:1213公里

典型场景:

  • 坐标、颜色值 (R, G, B)、配置信息
  • 函数返回多个值
  • 需要当字典的键
  • 数据不应该被修改

元组比列表快一点点,因为不可变,Python 不需要为修改预留空间。


6. 什么时候用字典?

按键查找、结构化数据、配置信息。

# 用户信息
user = {
"name": "陈默",
"age": 25,
"skills": ["Python", "SQL"],
"active": True,
}

# 快速查找
print(user["name"])          # 输出: 陈默
print("skills"in user)      # 输出: True

典型场景:

  • JSON 数据处理(API 返回的数据就是字典)
  • 配置信息、用户资料
  • 需要按名字而不是位置查找
  • 计数器(用键记种类,用值记数量)
# 计数器:统计每个字符出现的次数
text = "hello world"
counter = {}
for char in text:
if char != " ":
        counter[char] = counter.get(char, 0) + 1

print(counter)
# 输出: {'h': 1, 'e': 1, 'l': 3, 'o': 2, 'w': 1, 'r': 1, 'd': 1}

7. 什么时候用集合?

去重、交集并集、快速判断存在。

# 去重:统计独立访客
visits = ["user1", "user2", "user1", "user3", "user2"]
unique = set(visits)
print(f"独立用户:{len(unique)}个")    # 输出: 独立用户:3个

# 交集:共同好友
my_friends = {"小明", "小红", "小华", "小刚"}
your_friends = {"小红", "小李", "小华"}
common = my_friends & your_friends
print(f"共同好友:{common}")    # 输出: 共同好友:{'小红', '小华'}

# 差集:我独有的
only_mine = my_friends - your_friends
print(f"只有我认识的:{only_mine}")    # 输出: 只有我认识的:{'小明', '小刚'}

典型场景:

  • 去重
  • 判断元素是否存在(比列表快得多)
  • 集合运算:交集、并集、差集
  • 黑名单/白名单

8. 互相转换

四种数据结构可以互相转换。

data = [1, 2, 3, 2, 1, 4]

# 列表 → 集合(去重)
unique = set(data)
print(unique)           # 输出: {1, 2, 3, 4}

# 集合 → 列表(恢复顺序能力)
back_to_list = list(unique)
print(back_to_list)     # 输出: [1, 2, 3, 4]

# 列表 → 元组(锁定数据)
locked = tuple(data)
print(locked)           # 输出: (1, 2, 3, 2, 1, 4)

# 两个列表 → 字典
keys = ["name", "age", "job"]
values = ["陈默", 25, "程序员"]
person = dict(zip(keys, values))
print(person)           # 输出: {'name': '陈默', 'age': 25, 'job': '程序员'}

# 字典的键/值 → 列表
print(list(person.keys()))      # 输出: ['name', 'age', 'job']
print(list(person.values()))    # 输出: ['陈默', 25, '程序员']

9. 嵌套组合:真实世界的数据

实际开发中,数据结构往往嵌套使用。

# 列表里套字典:常见的数据格式
students = [
    {"name": "陈默", "scores": {"Python": 90, "SQL": 85}},
    {"name": "小明", "scores": {"Python": 78, "SQL": 92}},
    {"name": "小红", "scores": {"Python": 95, "SQL": 88}},
]

# 找出 Python 成绩最高的人
best = max(students, key=lambda s: s["scores"]["Python"])
print(f"Python 最高分:{best['name']}({best['scores']['Python']}分)")
# 输出: Python 最高分:小红(95分)

# 所有人名
names = [s["name"] for s in students]
print(names)    # 输出: ['陈默', '小明', '小红']
# 字典里套列表:标签系统
articles = {
"Python入门": ["编程", "Python", "教程"],
"数据结构": ["编程", "Python", "基础"],
"今天吃什么": ["生活", "美食"],
}

# 找所有和"编程"相关的文章
coding_articles = [title for title, tags in articles.items() if"编程"in tags]
print(coding_articles)    # 输出: ['Python入门', '数据结构']

# 所有标签(去重)
all_tags = set()
for tags in articles.values():
    all_tags.update(tags)
print(all_tags)    # 输出: {'编程', 'Python', '教程', '基础', '生活', '美食'}

10. 选择指南:一张图搞定

你需要什么?
│
├─ 按位置存取,可以修改 → 列表 []
│
├─ 按位置存取,不能修改 → 元组 ()
│
├─ 按名字查找 → 字典 {k: v}
│
└─ 去重 / 判断存在 / 集合运算 → 集合 set()

还有两个冷门但好用的:

  • collections.defaultdict:字典取不存在的键不报错,返回默认值
  • collections.Counter:专门用来计数的字典
  • collections.OrderedDict:保证插入顺序的字典(3.7+ 普通 dict 已经有序了)
from collections import Counter, defaultdict

# Counter:一行统计
words = ["苹果", "香蕉", "苹果", "橙子", "香蕉", "苹果"]
print(Counter(words))    # 输出: Counter({'苹果': 3, '香蕉': 2, '橙子': 1})

# defaultdict:不怕 KeyError
graph = defaultdict(list)
graph["北京"].append("上海")
graph["北京"].append("广州")
print(dict(graph))    # 输出: {'北京': ['上海', '广州']}

最后

数据结构的选择,直接决定了代码的质量。

选错了会怎样?

  • 用列表存 10 万个元素然后判断"在不在这个列表里"——慢到怀疑人生
  • 用字典存一组不需要查找的数据——浪费内存
  • 用可变列表存不该改的配置——出了 bug 找不到原因

记住一个原则:用最简单的结构,满足你的需求。

不需要修改就用元组,需要查找就用字典或集合,其他情况用列表。

我的建议:

回顾一下你最近写的代码,看看有没有用错数据结构的地方。试着替换成更合适的,对比一下性能和可读性。

好的代码,从选对容器开始。

今天就到这里。

我是陈默,我们下期再见。


如果你觉得这篇文章有帮助,欢迎关注我。我会持续分享 Python 学习的干货。

最新文章

随机文章