游乐游手机版
首页/编程语言/文章详情

Python字典与Set原理详解:哈希查找为什么这么快

时间:2026-08-15 07:04
Python 字典与 Set 详解:为什么 HashMap 查找这么快?JS 开发者必看 先说明一下这篇文章为什么值得认真读。不管你是 Python 入门者,还是从 JavaScript 转来的前端工程师,字典(Dict)和集合(Set)都是绕不开的核心数据结构。尤其对前端开发者来说,JS 中的对象

Python 字典与 Set 详解:为什么 HashMap 查找这么快?JS 开发者必看

先说明一下这篇文章为什么值得认真读。不管你是 Python 入门者,还是从 JavaScript 转来的前端工程师,字典(Dict)和集合(Set)都是绕不开的核心数据结构。尤其对前端开发者来说,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 的特点

特性DictList
查找速度O(1),极快O(n),数据越多越慢
插入速度O(1),通常很快O(1) 尾部插入快,其他位置较慢
内存占用大(哈希表需要额外空间)小,内存利用更节省
顺序无顺序(Python 3.7+ 保持插入顺序)有顺序
适用场景快速查找、键值映射、HashMap 场景有序存储、顺序遍历
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,通常是最高效也最容易理解的方式:

操作Ja vaScriptPython
创建{name: '张三', age: 18}{'name': '张三', 'age': 18}
访问obj.name 或 obj['name']d['name']
添加/修改obj.city = '北京'd['city'] = '北京'
删除delete obj.cityd.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 的查找原理、key 规则和哈希思想,也会让你更深入地理解 JavaScript 对象的工作机制。

来源:https://juejin.cn/post/7646235454803394575
上一篇mount命令中uid和gid参数设置方法详解 下一篇Cobbler镜像管理使用教程与配置方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Python应用打包与部署入门教程:核心概念、操作步骤与结果验证
编程语言 · 2026-10-01

Python应用打包与部署入门教程:核心概念、操作步骤与结果验证

从 Python 应用打包的基本概念入手,介绍项目环境准备、依赖管理、构建发布包、安装部署以及运行结果验证,并梳理常见打包失败与部署问题,帮助初学者完成从源码到可部署应用的完整流程。

Python CLI 开发避坑指南:从环境配置到参数解析的实战排查
编程语言 · 2026-10-01

Python CLI 开发避坑指南:从环境配置到参数解析的实战排查

本文聚焦 Python 命令行工具(CLI)开发中最高频的故障点,按执行链路梳理从环境配置、参数解析、路径处理到异常调试的完整排查流程。通过具体代码示例与终端输出对照,提供可复现的修复方案,帮助开发者快速定位 ModuleNotFoundError、参数校验失败及跨平台兼容性问题,构建更健壮的命令行

Python CLI 开发:从参数解析到工程化发布的完整路径
编程语言 · 2026-10-01

Python CLI 开发:从参数解析到工程化发布的完整路径

本文以 Python 命令行工具开发为切入点,从项目结构搭建与虚拟环境配置入手,深入讲解 argparse 参数解析与子命令设计。通过一个完整的日志分析工具案例,演示输入校验、错误处理与异常捕获的最佳实践,最后覆盖打包发布流程与常见排查技巧,帮助开发者构建健壮、易用的 CLI 应用。

Python 模块与包的工程化实践:结构、依赖与排错指南
编程语言 · 2026-10-01

Python 模块与包的工程化实践:结构、依赖与排错指南

本文从项目目录规范与模块导入机制切入,详细阐述虚拟环境的配置、第三方包的管理策略以及完整案例的模块化拆分方法。通过具体代码示例展示如何构建高内聚低耦合的代码结构,并针对 ModuleNotFoundError、ImportError 及依赖冲突等常见工程问题提供系统化的排查与解决方案,帮助开发者建立

Python 函数参数与返回值:从环境搭建到实战避坑
编程语言 · 2026-10-01

Python 函数参数与返回值:从环境搭建到实战避坑

本文从搭建 Python 运行环境入手,详细解析函数定义、参数传递机制及返回值处理。通过电商订单计算的完整案例,展示如何模块化组织业务逻辑,并针对参数数量、作用域及返回值缺失等常见错误提供排查方案,帮助开发者写出健壮且可维护的代码。