最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Python 字典与 Set 详解:HashMap 查找为何这么快?JS 开发者必看
时间:2026-06-03 08:35:01 编辑:袖梨 来源:一聚教程网
Python 的字典(Dict)与集合(Set)是基于哈希表实现的核心数据结构,其查找速度可达 O(1),这与 JavaScript 对象字面量的底层原理高度相通。本文将从哈希表原理出发,详细解析 Dict 与 Set 的工作机制,并对比 JS 相关特性,帮助开发者透彻理解其高效性。
Python 字典与 Set 详解:为什么 HashMap 查找这么快?JS 开发者必看
前言
当天的课程深入探讨了 Python 的 Dict(字典) 和 Set(集合),同时也涉及 JS 中函数提升的优先级话题。

身为前端开发者,我对 Dict 格外关注——因为 JS 中的对象字面量 {} 本质上正是一个字典或 HashMap。深入理解 Python Dict 的原理,反过来也能加深对 JS 对象的认知。
这篇文章将从哈希表原理出发,带你彻底搞懂 Dict 和 Set。
一、为什么需要 Dict?
1.1 一个实际问题
假设要根据同学的姓名查找成绩,不用字典该怎么做?
# 方法一:两个列表,下标对应
names = ['张三', '李四', '王五']
scores = [95, 99, 100]# 查找李四的成绩:先找索引,再找成绩
index = names.index('李四') # O(n) 遍历查找
score = scores[index] # O(1) 索引访问
问题: names.index() 需要从第一个元素遍历到最后一个,时间复杂度为 O(n)。
1.2 用 Dict 解决
# 方法二:字典,O(1) 直接查找
d = {'小红': 95, '小米': 75, '小虎': 50}
d['小虎'] # 50
瞬间完成! 这就像查字典的偏旁索引一样——O(1) 直接定位,无需从头遍历到尾。
二、哈希表(HashTable)原理
2.1 什么是哈希表?
哈希表工作原理key ──▶ 哈希函数 ──▶ 索引(地址) ──▶ value'小虎' ──▶ hash('小虎') ──▶ 地址 0x3F ──▶ 50
'小红' ──▶ hash('小红') ──▶ 地址 0x7A ──▶ 95
'小米' ──▶ hash('小米') ──▶ 地址 0x1C ──▶ 75查找 '小虎':hash('小虎') → 0x3F → 50 O(1)
2.2 为什么这么快?
查找方式对比List 查找:O(n)
┌───┬───┬───┬───┬───┐
│ 张 │ 李 │ 王 │ 赵 │ 刘 │ 从第一个找到最后一个
└───┴───┴───┴───┴───┘
↑
逐个比较...Dict 查找:O(1)
'王五' ──▶ hash() ──▶ 直接跳到目标位置
三、Python Dict 详解
3.1 基本操作
# 创建字典
d = {'小红': 95, '小米': 75, '小虎': 50}# 查找(O(1))
d['小虎'] # 50# 添加/修改
d['小蓝'] = 67 # 添加新键值对
d['小灰'] = 67 # 添加
d['小灰'] = 87 # 修改(key 已存在则更新)# 删除
d.pop('小灰') # 删除并返回值
3.2 安全访问:避免 KeyError
# 直接访问不存在的 key 会报错
d['小绿'] # KeyError: '小绿'# 方法一:in 判断
'小绿' in d # False# 方法二:get 方法
d.get('小绿') # None(不存在返回 None)
d.get('小绿', -1) # -1(不存在返回默认值)
d.get('小灰', -1) # 87(存在返回实际值)
3.3 Dict 的特点
| 特性 | Dict | List |
|---|---|---|
| 查找速度 | O(1),极快 | O(n),随元素增加变慢 |
| 插入速度 | O(1),极快 | O(1) 尾部插入快,其他位置慢 |
| 内存占用 | 大(哈希表需要额外空间) | 小,浪费内存少 |
| 顺序 | 无顺序(Python 3.7+ 保持插入顺序) | 有顺序 |
| 适用场景 | 高速查找、键值映射 | 有序存储、遍历 |
Dict vs List 权衡Dict:空间换时间
├── 速度快 O(1)
└── 内存消费多List:时间换空间
├── 速度随数据量增加而变慢 O(n)
└── 占用空间小
四、为什么 key 必须是不可变类型?
4.1 unhashable type 错误
key = [1, 2, 3] # list 是可变类型
d[key] = 'a list' # TypeError: unhashable type: 'list'
4.2 原理解释
为什么 key 必须不可变?哈希表通过 key 计算存储位置:
key ──▶ hash(key) ──▶ 地址如果 key 是可变的:
┌──────────────────────────────────────────────┐
│ 第 1 次计算:hash([1,2,3]) → 地址 A │
│ 存入值到地址 A │
│ │
│ 修改 key:[1,2,3] → [1,2,4] │
│ 第 2 次计算:hash([1,2,4]) → 地址 B(不同了!) │
│ │
│ 找不到之前存入的值了!Dict 混乱了 │
└──────────────────────────────────────────────┘
4.3 哪些类型可以作为 key?
| 类型 | 可否做 key | 原因 |
|---|---|---|
str 字符串 | 不可变 | |
int 整数 | 不可变 | |
tuple 元组 | 不可变 | |
list 列表 | 可变(unhashable) | |
dict 字典 | 可变(unhashable) | |
set 集合 | 可变(unhashable) |
五、Set:没有 value 的 Dict
5.1 什么是 Set?
Dict vs SetDict: {key: value, key: value, ...}
Set: {key, key, key, ...} ← 只有 key,没有 valueSet 的原理和 Dict 完全一样,只是不存 value
5.2 基本操作
# 创建 Set
s = {1, 2, 3}
# 或从列表创建(自动去重)
s = set([1, 2, 3, 2, 5]) # {1, 2, 3, 5}# 添加
s.add(4) # {1, 2, 3, 4}# 删除
s.remove(4) # {1, 2, 3}
5.3 集合运算
s1 = {1, 2, 3}
s2 = {2, 3, 4}s1 & s2 # {2, 3} 交集
s1 | s2 # {1, 2, 3, 4} 并集
集合运算图示s1 = {1, 2, 3} s2 = {2, 3, 4}交集 & : 并集 | :
┌───┐ ┌───┬───┬───┐
│ 2 │ │ 1 │ 2 │ 3 │ 4
│ 3 │ └───┴───┴───┘
└───┘
5.4 Set 天然去重
# 从列表创建 Set,自动去重
s = set([1, 2, 3, 2, 5])
# 结果:{1, 2, 3, 5} ← 2 被去重了
六、可变对象 vs 不可变对象
6.1 核心区别
可变对象 vs 不可变对象不可变对象(Immutable)
├── str 字符串
├── int 整数
├── tuple 元组
└── 操作不会改变对象本身,而是返回新对象可变对象(Mutable)
├── list 列表
├── dict 字典
├── set 集合
└── 操作会直接修改对象本身
6.2 代码验证
字符串(不可变)
str = 'abc'
print(str.replace('a', 'A')) # 'Abc' ← 返回新字符串
print(str) # 'abc' ← 原字符串没变!
原理: 'abc'.replace('a', 'A') 并未修改 'abc' 自身内容,而是返回了一个全新的字符串对象 'Abc'。
str ──▶ 'abc'(不变)
│
│ replace('a', 'A')
▼
'Abc'(新对象,需要用变量接收)
列表(可变)
a = ['c', 'b', 'a']
print(a.sort()) # None ← sort 原地修改,不返回新列表
a # ['a', 'b', 'c'] ← 原列表被修改了
原理: a.sort() 直接改变了原列表的内容,a 仍然指向同一块内存地址。
a ──▶ ['c', 'b', 'a']
│
│ sort() 原地修改
▼
['a', 'b', 'c'](同一块内存,内容变了)
七、JS 补充:函数提升的优先级
7.1 函数声明 vs 函数表达式同名
当函数声明和函数表达式同名时,函数声明优先提升:
showName();
function showName() {
console.log(1); // 输出 1
}
var showName = function() {
console.log(2);
}
为什么输出 1?
编译阶段(模拟提升后):// 1. 函数声明优先提升
function showName() {
console.log(1);
}// 2. 变量声明提升(但被函数声明覆盖)
var showName; // showName 已经是函数了// 3. 执行阶段
showName(); // 调用的是函数声明 → 输出 1// 4. 赋值(在调用之后才执行)
showName = function() {
console.log(2);
};
7.2 两个同名函数声明
如果定义了两个相同的函数声明,后定义的会覆盖先定义的:
function showName() {
console.log(1);
}
showName(); // 2 ← 输出 2!
function showName() {
console.log(2);
}
showName(); // 2
编译阶段提升后:function showName() {
console.log(1);
}
function showName() {
console.log(2); // ← 后面的覆盖前面的
}执行:
showName(); // 2
showName(); // 2
八、JS 对象字面量 vs Python Dict 对比
对于前端开发者而言,用 JS 类比学习 Python Dict 最为高效:
| 操作 | JavaScript | Python |
|---|---|---|
| 创建 | {name: '张三', age: 18} | {'name': '张三', 'age': 18} |
| 访问 | obj.name 或 obj['name'] | d['name'] |
| 添加/修改 | obj.city = '北京' | d['city'] = '北京' |
| 删除 | delete obj.city | d.pop('city') |
| 安全访问 | obj.city || '未知' | d.get('city', '未知') |
| 判断存在 | 'city' in obj | 'city' in d |
| key 类型 | 字符串/Symbol | 不可变类型(str/int/tuple) |
| Set 创建 | new Set([1,2,3]) | set([1,2,3]) |
| Set 交集 | 无内置 | s1 & s2 |
| Set 并集 | 无内置 | s1 | s2 |
九、知识图谱
Dict / Set 知识图谱哈希表原理
├── key 通过哈希函数计算索引
├── O(1) 查找和插入
├── 空间换时间
└── key 必须不可变(hashable)Python Dict
├── 创建:{key: value}
├── 访问:d[key]
├── 安全访问:d.get(key, default)
├── 判断存在:key in d
├── 添加/修改:d[key] = value
├── 删除:d.pop(key)
└── key 必须是不可变类型Python Set
├── 创建:{1,2,3} 或 set([1,2,3])
├── 天然去重
├── 添加:s.add(x)
├── 删除:s.remove(x)
├── 交集:s1 & s2
└── 并集:s1 | s2可变 vs 不可变
├── 不可变:str, int, tuple
│ └── 操作返回新对象
└── 可变:list, dict, set
└── 操作修改原对象JS 补充
├── 函数声明提升优先于变量
├── 同名函数声明后者覆盖前者
└── JS 对象字面量 ≈ Python Dict
结语
Dict 和 Set 是 Python 中最常用的数据结构之一,理解其底层哈希表原理,能在合适场景下做出正确选择。对于前端开发者来说,Python Dict 与 JS 对象字面量本质上是同一种数据结构,掌握 Dict 原理也能更深入地理解 JS 对象的工作方式。
相关文章
- 扫福必得敬业福的福字图片 06-18
- 2026年DeepSeek使用要点:账号、权限与入口说明 06-18
- DeepSeek响应缓慢:网络环境与模型配置排查说明 06-18
- 容易能扫出敬业福福字图片大全-2026必出敬业福福字图最新 06-18
- 2026年Grok收费吗?免费版与会员订阅功能差异说明 06-18
- Kimi内容生成版权风险:使用场景与合规要点说明 06-18