Python 数据结构的本质:位置、对象引用、Hash 与可变性

Python 中不同的数据类型,虽然都可以保存数据,但它们管理数据的方式并不一样。

真正需要理解的是:

这个数据结构靠什么找到和管理里面的数据?


1. List:按照位置管理元素

python 复制代码
lst = [10, 20, 30]

可以理解为:

text 复制代码
位置 0 → 10
位置 1 → 20
位置 2 → 30

所以可以:

python 复制代码
lst[1]

得到:

text 复制代码
20

也可以:

python 复制代码
lst[1] = 100

变成:

python 复制代码
[10, 100, 30]

因此:

list 按位置管理元素,每个位置都可以重新指向其他对象。


2. Tuple:位置存在,但位置指向的对象不能更换

python 复制代码
t = (10, 20, 30)

同样可以理解为:

text 复制代码
位置 0 → 10
位置 1 → 20
位置 2 → 30

所以可以:

python 复制代码
t[1]

但不能:

python 复制代码
t[1] = 100

因为 tuple 创建以后:

每一个位置所指向的对象不能再被换掉。

但是:

python 复制代码
t = (10, [20, 30])

是允许的。

此时:

text 复制代码
位置 0 → 10
位置 1 → [20, 30]

可以:

python 复制代码
t[1].append(40)

得到:

python 复制代码
(10, [20, 30, 40])

因为 t[1] 仍然指向原来的 list,只是 list 自己内部的数据发生了变化。

因此:

tuple 的不可变,是位置所指向的对象不能更换,并不代表被指向的对象本身一定不能变化。


3. Set:按照 Hash 管理成员

python 复制代码
s = {10, 20, 30}

set 没有:

python 复制代码
s[0]

因为 set 不是按照位置来管理元素。

元素加入 set 时,Python 会:

text 复制代码
元素
→ 计算 hash
→ 根据 hash 计算内部存储位置
→ 存入 set

例如:

text 复制代码
10 → hash(10) → 计算位置 → 存放 10
20 → hash(20) → 计算位置 → 存放 20
30 → hash(30) → 计算位置 → 存放 30

查询:

python 复制代码
20 in s

大致过程:

text 复制代码
20
→ 计算 hash(20)
→ 根据 hash 找到可能的位置
→ 检查是不是 20
→ 返回 True / False

所以:

set 时按照 hash 管理成员,而不是按下标管理元素。

这也是为什么 set 中的元素必须可以被 hash()。

例如:

python 复制代码
hash(10)        # 可以
hash("hello")   # 可以

所以它们可以放入 set。

而:

python 复制代码
hash([1, 2])

会报错:

text 复制代码
TypeError: unhashable type: 'list'

因此 list 不能直接作为 set 的元素。


4. Dict:Key 按照 Hash 管理,Value 跟着 Key 保存

python 复制代码
d = {
    "name": "Tom",
    "age": 20
}

dict 不是按照:

text 复制代码
第 0 个
第 1 个

来查找数据。

它主要根据 key 的 hash 管理数据。

例如:

python 复制代码
d["name"]

Python 大致会:

text 复制代码
"name"
→ 计算 hash("name")
→ 根据 hash 找到内部位置
→ 找到 key "name"
→ 得到对应的 value "Tom"

所以字典内部管理的是:

text 复制代码
key + value

其中:

key 负责定位,value 跟着 key 保存。

因此字典的 key 必须可哈希:

python 复制代码
{
    "name": "Tom",   # str 可以
    10: "hello"      # int 可以
}

但不能:

python 复制代码
{
    [1, 2]: "hello"
}

因为 list 不可哈希。

而 value 没有这个限制:

python 复制代码
d = {
    "data": [1, 2, 3]
}

完全可以。

因为真正用来定位的是:

text 复制代码
"data"

而不是后面的 list。


5. String:按照位置读取字符,但字符串本身不能修改

python 复制代码
s = "Python-DuDu"

可以理解为:

text 复制代码
位置 0 → P
位置 1 → y
位置 2 → t
位置 3 → h
位置 4 → o
位置 5 → n
...

所以可以:

python 复制代码
s[0]

得到:

text 复制代码
P

但是不能:

python 复制代码
s[0] = "J"

因为字符串是不可变的。

如果写:

python 复制代码
s = "DuDu"

并不是把原来的 "Python" 修改成 "DuDu"。

而是让变量 s 改去指向新的字符串对象。

因此:

str 按照位置读取,但字符串创建以后内容不能修改。

字符串也是可哈希的:

python 复制代码
hash("Python")

所以字符串可以作为:

text 复制代码
set 元素
dict 的 key

6. 为什么 List、Dict、Set 本身不能 Hash?

它们都有一个共同特点:

自身内容可以直接发生变化。

例如:

python 复制代码
lst.append(10)
python 复制代码
d["age"] = 30
python 复制代码
s.add(40)

如果这种对象作为哈希表中的元素或 key,而它们的内容变化又导致 hash 发生变化,就可能导致:

text 复制代码
存进去时使用一个 hash
后来查找时变成另一个 hash

这样原来的位置就可能找不到。

所以 Python 将常见的:

text 复制代码
list
dict
set

设计成不可哈希。


7. 常见类型总结

类型 主要管理方式 是否可修改 是否可 Hash
list 按位置管理元素 是 否
tuple 按位置管理,位置所指向的对象不能更换 否 内部所有元素都可 Hash 时可以
set 根据元素的 Hash 计算内部存储和查找位置 是 set 本身不可以
dict 根据 Key 的 Hash 计算内部存储和查找位置 是 dict 本身不可以
str 按位置读取字符 否 可以
int 单个不可变值 否 可以
frozenset 根据元素的 Hash 计算内部存储和查找位置 否 可以

8. 总结

  • List:按位置存取,每个位置都可以重新指向其他对象。

  • Tuple :也按位置存取,但每个位置所指向的对象不能被替换。

    不过,被指向的对象本身可以是可变对象;如果对象本身可变,其内部数据仍然可以发生改变。

  • String:按位置读取,但自身内容不能修改。

  • Set :根据元素的 Hash 进行存储和查找。

  • Dict :根据 Key 的 Hash 进行存储和查找。

  • Set 的元素 和 Dict 的 Key 必须可哈希。

    在 Python 常见内置类型中,可以简单理解为:

    • 不可变类型通常可哈希
    • 可变类型通常不可哈希

需要注意:

Tuple 虽然属于 Python 的不可变数据类型,但如果其中包含不可哈希的对象,例如 List,那么这个 Tuple 整体也不可哈希。

相关推荐
不会就选b1 小时前
算法日常・每日刷题--<动态规划>1
java·数据结构·算法
shehuiyuelaiyuehao1 小时前
算法54,链表 两数相加
数据结构·算法·链表
李航19831 小时前
自动动手开发图形引擎,不仅能AI建模,还能AI渲染
人工智能·python·计算机视觉·ai·ai编程
LeoCrawls1 小时前
Python 读取 JSON 常见报错排查,附完整处理函数
python·json·php
zzZ··*1 小时前
CodeBuddy 用量看板:本地解析 Token 与积分消耗,不联网不上传
python·vue·ai编程
Logic1012 小时前
C语言/数据结构位运算题解:异或XOR找出弹子游戏机中数量为奇数的数字——只出现奇数次
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
小猴子爱上树2 小时前
跨马翻译:批量图片翻译+视频字幕+智能抠图一站式工具
python·音视频
小朱爱编程1232 小时前
我用 Jev 做了三个实用工具:整理标签页、分诊飞书反馈、找回 GitHub 收藏
java·开发语言·人工智能·后端·python·架构·ai编程
TomEval2 小时前
【测AI】第06篇:数据清洗实战 —— Pandas 处理爬取的 JD 数据
人工智能·python·自动化·aigc·pandas