Python字典dict内部实现机制是怎样的?

2026-05-21 17:161阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2115个文字,预计阅读时间需要9分钟。

Python字典dict内部实现机制是怎样的?

一、什么是字典?字典是一系列由键(key)和值(value)组成的元素集合,每个元素通过键值对的方式存储信息。字典是一个可变容器模型,可以存储任意类型的对象。

二、字典的特点:

1.字典中的元素是唯一的,每个键(key)对应一个唯一的值(value)。

2.字典的键(key)必须是不可变类型,如整数、浮点数、字符串、元组等。

3.字典的值(value)可以是任意类型,包括列表、字典、字符串、数字等。

三、字典的应用:

1.字典常用于存储具有映射关系的数据,如姓名与电话号码、学生信息等。

2.字典可以方便地通过键来查找对应的值,具有快速的查找效率。

四、字典的实现:

1.字典是通过哈希表实现的,具有高效的查找效率。

2.字典中的键值对是无序的,在Python 3.7版本中,字典元素将根据插入顺序进行排列。

五、Python中字典的示例代码:

python创建一个字典my_dict={ 'name': '张三', 'age': 18, 'city': '北京'}

查找键对应的值name=my_dict['name']print(name) # 输出:张三

修改字典中的键值对my_dict['age']=19print(my_dict['age']) # 输出:19

删除字典中的键值对del my_dict['city']print(my_dict) # 输出:{'name': '张三', 'age': 19}


一. 什么是字典?

字典是一系列由键(key)和值(value)配对组成的元素的集合。字典是一个可变容器模型,可以存储任意类型对象。字典实现与哈希算法密不可分(不同的Python版本,算法会不同),不了解哈希算法的童鞋可以先去了解相关知识。

Python字典dict内部实现机制是怎样的?

二. 字典是否是有序的?

在Python3.6之前,字典是无序的,但是Python3.7+,字典是有序的。在3.6中,字典有序是一个implementation detail,在3.7才正式成为语言特性,因此3.6中无法确保100%有序。

三. 字典的查询、添加、删除的时间复杂度?

字典的查询、添加、删除的平均时间复杂度都是O(1)(为什么是平均时间复杂度,文章后面会讲解到),相比列表与元祖,性能更优。

四. 字典的实现原理?

  • 首先说说Python3.6之前的无序字典
  • 字典底层是维护一张哈希表(见下图),我们可以把哈希表看成一个列表,哈希表中的每一个元素又存储了哈希值(hash)、键(key)、值(value)3个元素。(Python3.6之前)

    enteies = [
    ['--', '--', '--'],
    [hash, key, value],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [hash, key, value],
    ]

    由上可以见哈希表的存储结构,我们也可以看出,元素之间有一些空元素,我们通过增加一个元素来讲解具体实现。

    • 计算key的hash值,再和mask做与操作,运算后会得到一个数字,这个index就是要插入的enteies哈希表中的下标位置
    • 若index下标位置已经被占用,则会判断enteies的key是否与要插入的key是否相等
    • 如果key相等就表示key已存在,则更新value值
    • 如果key不相等,就表示hash冲突,则会继续向下寻找空位置,一直到找到剩余空位为止。

    以上介绍了老字典的实现过程,下面我们带入具体的数值来介绍。

    # 给字典添加一个值,key为hello,value为word
    my_dict['hello'] = 'word'

    # 假设是一个空列表,hash表初始如下
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ]

    hash_value = hash('hello') # 假设值为 12343543 注:以下计算值不等于实际值,仅为演示使用
    index = hash_value & ( len(enteies) - 1) # 假设index值计算后等于3,具体的hash算法本文不做介绍

    # 下面会将值存在enteies中
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [12343543, 'hello', 'word'], # index=3
    ['--', '--', '--'],
    ]

    # 我们继续向字典中添加值
    my_dict['color'] = 'green'

    hash_value = hash('color') # 假设值为 同样为12343543
    index = hash_value & ( len(enteies) - 1) # 假设index值计算后同样等于3

    # 下面会将值存在enteies中
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [12343543, 'hello', 'word'], # 由于index=3的位置已经被占用,且key不一样,所以判定为hash冲突,继续向下寻找
    [12343543, 'color', 'green'], # 找到空余位置,则保存
    ]

    通过上面的讲解,已经了解了字典的插入的过程,可以更具此过程分析出字典查找、插入的执行过程,这里就不过多赘述。我们可以看到,不同的key计算的出的index值是不一样的,在enteies中插入的位置不一样,所以当我们遍历字典的时候,字段的顺序与我们插入的顺序是不相同的。

    我们同样可以发现,enteies表是稀疏的,随着我们插入的值不同,enteies表会越来越稀疏(enteies也是一个会动态扩展长度的,每一此扩展长度,都会重新计算所有key的hash值),所以新的字典实现就随之出现。

    2. Python3.7+后的新的实现方式

    老字典使用一张hash,而新字典还使用了一张Indices表来辅助。下来列出新的结构:

    indices = [None, None, index, None, index, None, index]
    enteies = [
    [hash0, key0, value0],
    [hash1, key1, value1],
    [hash2, key2, value2]
    ]

    下面为具体的实现过程:

    • 计算key的hash值,再和mask做与操作,运算后会得到一个数字,这个index就是要插入的indices的下标位置(注:具体算法与Python版本相关,并不一定一样)
    • 得到index后,会找到indices的位置,但是此位置不是存的hash值,而是存的len(enteies),表示该值在enteies中的位置
    • 如果出现hash冲突,则处理方式与老字典处理方式类似

    下面带入实际实现过程:

    # 给字典添加一个值,key为hello,value为word
    my_dict['hello'] = 'word'

    # 假设是一个空列表,hash表初始如下
    indices = [None, None, None, None, None, None]
    enteies = []

    hash_value = hash('hello') # 假设值为 12343543
    index = hash_value & ( len(indices) - 1) # 假设index值计算后等于3,具体的hash算法本文不做介绍

    # 会找到indices的index为3的位置,并插入enteies的长度
    indices = [None, None, None, 0, None, None]
    # 此时enteies会插入第一个元素
    enteies = [
    [12343543, 'hello', 'word']
    ]

    # 我们继续向字典中添加值
    my_dict['haimeimei'] = 'lihua'

    hash_value = hash('haimeimei') # 假设值为 34323545
    index = hash_value & ( len(indices) - 1) # 假设index值计算后同样等于 0

    # 会找到indices的index为0的位置,并插入enteies的长度
    indices = [1, None, None, 0, None, None]
    # 此时enteies会插入第一个元素
    enteies = [
    [12343543, 'hello', 'word'],
    [34323545, 'haimeimei', 'lihua']
    ]

    我们在来看一下查询字典的具体过程:

    # 下面是一个字典与字典的存储
    more_dict = {'name': '张三', 'sex': '男', 'age': 10, 'birth': '2019-01-01'}

    # 数据实际存储
    indices = [None, 2, None, 0, None, None, 1, None, 3]
    enteies = [
    [34353243, 'name', '张三'],
    [34354545, 'sex', '男'],
    [23343199, 'age', 10],
    [00956542, 'birth', '2019-01-01'],
    ]

    print(more_dict['age']) # 当我们执行这句时

    hash_value = hash('age') # 假设值为 23343199
    index = hash_value & ( len(indices) - 1) # index = 1

    entey_index = indices[1] # 数据在enteies的位置是2
    value = enteies[entey_index] # 所以找到值为 enteies[2]

    由上可以看出,新字典存储数据本身的enteies并不会稀疏,由indices来维护具体存储的位置,enteies中的数据是和插入的数据是一样的,所以新的字典是有序的。

    上面的例子没有说明冲突的解决方案,大家可以自己思考思考。

    五. 时间复杂度说明

    我们在上面提到了,字典的平均时间复杂度是O(1),因为字典是通过哈希算法来实现的,哈希算法不可避免的问题就是hash冲突,Python字典发生哈希冲突时,会向下寻找空余位置,直到找到位置。如果在计算key的hash值时,如果一直找不到空余位置,则字典的时间复杂度就变成了O(n)了,所以Python的哈希算法就显得非常重要了。Python字典的哈希算法,会尽量保证哈希值计算出的index是平均分布且每一个值之间有剩余位置,例如:

    [index, None, None, None, index, None, None, None]

    及index尽量只为 0, 3, 5, 7类似值,保证在发生哈希冲突时,能很快的找到空余位置。

    六. 字典的key能使用什么值?

    Python字典的key可以使用字符串(str),整型(int),元祖(tuple)等。我们已经知道,字典是通过哈希算法来计算key的值,所以key必须为可哈希的,list不能作为字典的key,因为list是可变的及不可哈希的对象,所以不能作为字典的key。

    本文共计2115个文字,预计阅读时间需要9分钟。

    Python字典dict内部实现机制是怎样的?

    一、什么是字典?字典是一系列由键(key)和值(value)组成的元素集合,每个元素通过键值对的方式存储信息。字典是一个可变容器模型,可以存储任意类型的对象。

    二、字典的特点:

    1.字典中的元素是唯一的,每个键(key)对应一个唯一的值(value)。

    2.字典的键(key)必须是不可变类型,如整数、浮点数、字符串、元组等。

    3.字典的值(value)可以是任意类型,包括列表、字典、字符串、数字等。

    三、字典的应用:

    1.字典常用于存储具有映射关系的数据,如姓名与电话号码、学生信息等。

    2.字典可以方便地通过键来查找对应的值,具有快速的查找效率。

    四、字典的实现:

    1.字典是通过哈希表实现的,具有高效的查找效率。

    2.字典中的键值对是无序的,在Python 3.7版本中,字典元素将根据插入顺序进行排列。

    五、Python中字典的示例代码:

    python创建一个字典my_dict={ 'name': '张三', 'age': 18, 'city': '北京'}

    查找键对应的值name=my_dict['name']print(name) # 输出:张三

    修改字典中的键值对my_dict['age']=19print(my_dict['age']) # 输出:19

    删除字典中的键值对del my_dict['city']print(my_dict) # 输出:{'name': '张三', 'age': 19}


    一. 什么是字典?

    字典是一系列由键(key)和值(value)配对组成的元素的集合。字典是一个可变容器模型,可以存储任意类型对象。字典实现与哈希算法密不可分(不同的Python版本,算法会不同),不了解哈希算法的童鞋可以先去了解相关知识。

    Python字典dict内部实现机制是怎样的?

    二. 字典是否是有序的?

    在Python3.6之前,字典是无序的,但是Python3.7+,字典是有序的。在3.6中,字典有序是一个implementation detail,在3.7才正式成为语言特性,因此3.6中无法确保100%有序。

    三. 字典的查询、添加、删除的时间复杂度?

    字典的查询、添加、删除的平均时间复杂度都是O(1)(为什么是平均时间复杂度,文章后面会讲解到),相比列表与元祖,性能更优。

    四. 字典的实现原理?

  • 首先说说Python3.6之前的无序字典
  • 字典底层是维护一张哈希表(见下图),我们可以把哈希表看成一个列表,哈希表中的每一个元素又存储了哈希值(hash)、键(key)、值(value)3个元素。(Python3.6之前)

    enteies = [
    ['--', '--', '--'],
    [hash, key, value],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [hash, key, value],
    ]

    由上可以见哈希表的存储结构,我们也可以看出,元素之间有一些空元素,我们通过增加一个元素来讲解具体实现。

    • 计算key的hash值,再和mask做与操作,运算后会得到一个数字,这个index就是要插入的enteies哈希表中的下标位置
    • 若index下标位置已经被占用,则会判断enteies的key是否与要插入的key是否相等
    • 如果key相等就表示key已存在,则更新value值
    • 如果key不相等,就表示hash冲突,则会继续向下寻找空位置,一直到找到剩余空位为止。

    以上介绍了老字典的实现过程,下面我们带入具体的数值来介绍。

    # 给字典添加一个值,key为hello,value为word
    my_dict['hello'] = 'word'

    # 假设是一个空列表,hash表初始如下
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    ]

    hash_value = hash('hello') # 假设值为 12343543 注:以下计算值不等于实际值,仅为演示使用
    index = hash_value & ( len(enteies) - 1) # 假设index值计算后等于3,具体的hash算法本文不做介绍

    # 下面会将值存在enteies中
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [12343543, 'hello', 'word'], # index=3
    ['--', '--', '--'],
    ]

    # 我们继续向字典中添加值
    my_dict['color'] = 'green'

    hash_value = hash('color') # 假设值为 同样为12343543
    index = hash_value & ( len(enteies) - 1) # 假设index值计算后同样等于3

    # 下面会将值存在enteies中
    enteies = [
    ['--', '--', '--'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [12343543, 'hello', 'word'], # 由于index=3的位置已经被占用,且key不一样,所以判定为hash冲突,继续向下寻找
    [12343543, 'color', 'green'], # 找到空余位置,则保存
    ]

    通过上面的讲解,已经了解了字典的插入的过程,可以更具此过程分析出字典查找、插入的执行过程,这里就不过多赘述。我们可以看到,不同的key计算的出的index值是不一样的,在enteies中插入的位置不一样,所以当我们遍历字典的时候,字段的顺序与我们插入的顺序是不相同的。

    我们同样可以发现,enteies表是稀疏的,随着我们插入的值不同,enteies表会越来越稀疏(enteies也是一个会动态扩展长度的,每一此扩展长度,都会重新计算所有key的hash值),所以新的字典实现就随之出现。

    2. Python3.7+后的新的实现方式

    老字典使用一张hash,而新字典还使用了一张Indices表来辅助。下来列出新的结构:

    indices = [None, None, index, None, index, None, index]
    enteies = [
    [hash0, key0, value0],
    [hash1, key1, value1],
    [hash2, key2, value2]
    ]

    下面为具体的实现过程:

    • 计算key的hash值,再和mask做与操作,运算后会得到一个数字,这个index就是要插入的indices的下标位置(注:具体算法与Python版本相关,并不一定一样)
    • 得到index后,会找到indices的位置,但是此位置不是存的hash值,而是存的len(enteies),表示该值在enteies中的位置
    • 如果出现hash冲突,则处理方式与老字典处理方式类似

    下面带入实际实现过程:

    # 给字典添加一个值,key为hello,value为word
    my_dict['hello'] = 'word'

    # 假设是一个空列表,hash表初始如下
    indices = [None, None, None, None, None, None]
    enteies = []

    hash_value = hash('hello') # 假设值为 12343543
    index = hash_value & ( len(indices) - 1) # 假设index值计算后等于3,具体的hash算法本文不做介绍

    # 会找到indices的index为3的位置,并插入enteies的长度
    indices = [None, None, None, 0, None, None]
    # 此时enteies会插入第一个元素
    enteies = [
    [12343543, 'hello', 'word']
    ]

    # 我们继续向字典中添加值
    my_dict['haimeimei'] = 'lihua'

    hash_value = hash('haimeimei') # 假设值为 34323545
    index = hash_value & ( len(indices) - 1) # 假设index值计算后同样等于 0

    # 会找到indices的index为0的位置,并插入enteies的长度
    indices = [1, None, None, 0, None, None]
    # 此时enteies会插入第一个元素
    enteies = [
    [12343543, 'hello', 'word'],
    [34323545, 'haimeimei', 'lihua']
    ]

    我们在来看一下查询字典的具体过程:

    # 下面是一个字典与字典的存储
    more_dict = {'name': '张三', 'sex': '男', 'age': 10, 'birth': '2019-01-01'}

    # 数据实际存储
    indices = [None, 2, None, 0, None, None, 1, None, 3]
    enteies = [
    [34353243, 'name', '张三'],
    [34354545, 'sex', '男'],
    [23343199, 'age', 10],
    [00956542, 'birth', '2019-01-01'],
    ]

    print(more_dict['age']) # 当我们执行这句时

    hash_value = hash('age') # 假设值为 23343199
    index = hash_value & ( len(indices) - 1) # index = 1

    entey_index = indices[1] # 数据在enteies的位置是2
    value = enteies[entey_index] # 所以找到值为 enteies[2]

    由上可以看出,新字典存储数据本身的enteies并不会稀疏,由indices来维护具体存储的位置,enteies中的数据是和插入的数据是一样的,所以新的字典是有序的。

    上面的例子没有说明冲突的解决方案,大家可以自己思考思考。

    五. 时间复杂度说明

    我们在上面提到了,字典的平均时间复杂度是O(1),因为字典是通过哈希算法来实现的,哈希算法不可避免的问题就是hash冲突,Python字典发生哈希冲突时,会向下寻找空余位置,直到找到位置。如果在计算key的hash值时,如果一直找不到空余位置,则字典的时间复杂度就变成了O(n)了,所以Python的哈希算法就显得非常重要了。Python字典的哈希算法,会尽量保证哈希值计算出的index是平均分布且每一个值之间有剩余位置,例如:

    [index, None, None, None, index, None, None, None]

    及index尽量只为 0, 3, 5, 7类似值,保证在发生哈希冲突时,能很快的找到空余位置。

    六. 字典的key能使用什么值?

    Python字典的key可以使用字符串(str),整型(int),元祖(tuple)等。我们已经知道,字典是通过哈希算法来计算key的值,所以key必须为可哈希的,list不能作为字典的key,因为list是可变的及不可哈希的对象,所以不能作为字典的key。