免费视频教程!零基础学Python系列(11) - 数据类型之字典

本节我们接着讲字典(Dictionary)类型:

这个系列教程建议对照着视频学习,以下仅为课件内容。

本节课程的视频和实例源码下载方式:点击->我的主页,查看个人简介。

我尽量坚持每日更新一节。


字典,也是Python中使用得比较广泛的一种数据类型。

字典本质上是一种“键值对”(key-value)的集合。“键值对”这种数据的描述方式,更加符合我们对客观世界的认识。客观世界的数据,通常都存在一个名字-key,以及对应的值-value,使用“键值对”可以非常直观简便地表达这些数据。一些数据库技术也是基于键值对的数据存储方式,比如Redis、memcached。“键值对”在大数据领域也有广泛应用。

字典的结构定义和JSON基本一致,两者可以很方便的相互转换。

 

字典的语法如下:

{key1: value1, key2: value2, key3: value3, …}

字典使用大括号{}括起来,每个key-value之间使用逗号分割。Key和value之间使用冒号:分割。

同一个字典里面,key值需要保证唯一,不能重复,并且只能是不可变数据类型(数字、字符串、元组)。

Value不要求唯一,并且可以是任意数据类型。

 

  • 了解hash结构:

字典是一种hash结构,我们有必要简单了解一下hash结构。下图是一个hash结构的示意(这不是python的真正实现):

 

hash是一种数据结构,准确说是一种数据的组织方式。我们在编程中,为了便于数据的操作(增删改查),尤其是性能考虑,会将数据对象以某种特别的方式组织起来,这就是数据结构。除了hash以为,还有数组array、链表sll\dll、平衡二叉树avl、红黑树rbt等。这些数据结构,是编程的算法范畴,不是编程语言的语法范畴,不同的编程语言都可以实现这些数据结构。我们不会重点去讲这些算法的东西,但是作为程序员,你需要理解这些常用的算法。如果有必要我后面可以专门制作一个系列来讲算法。

上图是我工作中最常见的hash结构。它定义了一个hash表(核心是一个定长的数组),用于存hash_key,由于它是数组,所以可以通过索引来查找,性能高。数据对象的key,通过一个hash函数映射到hash_key,这个映射关系可能是n:1,所以存在一个hash_key对应多个数据对象的情况,这就是所谓的hash冲突。为了解决hash冲突,我们在每个hash_key下面挂了一个链表结构,这个就是hash桶(bucket)。Hash冲突越严重,bucket就会越深,从而导致查找性能就越低,所以hash函数是关键。衡量一个hash函数的好坏,是它能否将所有数据对象均衡的散列到不同的hash_key上面的,并且hash函数本身不能耗费太多性能。

 

Hash结构,是各种操作(增删改查)性能都比较高比较均衡的一种结构,使用较广泛。

 

  • 字典和列表的区别

字典不是一种序列,它没有索引,并且是无序的,这一点和列表存在本质区别,大家要注意。

实质上,字典在python的底层实现中,是一种hash索引结构。所以字典结构在查询、新增、删除、修改上面表现出更加优异的时间性能。

 

而列表在python的底层实现中,是一种动态数组结构。该数组元素不会直接保存列表元素的数据,而是存储了列表元素的地址。地址的大小是固定的,所以它可以是一个数组结构。由于它是动态数组(支持resize内存空间),所以也可以支持扩容。同时,数组是连续存储的,所以列表存在索引,是有序的。

下图示意了字典和列表的区别,不同的python版本可能存在差异,但是大体类似。

 

列表采用了指针数组,它是一段连续内存,所以它可以像数组一样去索引的。而字典采用了hash表结构,key值是通过hash函数去命中到一个hash index,所以它只能通过key值去查找。

真正的实现中,不会像我画的这么简单。比如字典的hash表还需要考虑hash散列、hash冲突,但是这些细节并不影响我们理解字典和列表之间的本质区别。对于初学者,不建议大家现在去搞懂这些细节,可以留待后面再去深入理解。

 

下面我们通过一系列实例来熟悉字典的操作:


# file: ./5/5_8.py

# 字典
dict_1 = {'name': 'xiaowang', 'age': 20, 'city': 'Chengdu'}
print('name: %s, age: %d, city: %s' % (dict_1['name'], dict_1['age'], dict_1['city']))

dict_1['city'] = 'Beijing'
print('name: %s, age: %d, city: %s' % (dict_1['name'], dict_1['age'], dict_1['city']))

dict_2 = {}.fromkeys(('name', 'age', 'city'))  # 批量根据key值初始化一个字典
print(dict_2)

print(len(dict_1))

# 增加键值对
dict_1['sex'] = 'male'
print(dict_1)

# 删除键值对
del dict_1['sex']
print(dict_1)

# 批量更新
dict_3 = {'name': 'xiaoliu', 'age': 18, 'city': 'Chengdu', 'sex': 'female'}
dict_1.update(dict_3)
print(dict_1)

# 判断key是否在字典中存在
print('name' in dict_3)

# 获取所有的key
print(list(dict_1.keys()))

# 获取所有的value
print(list(dict_1.values()))

# 获取所有的(键, 值) 元组列表
print(list(dict_1.items()))

# 清空字典
dict_1.clear()
print(dict_1)

 

  • 深复制和浅复制

python提供了copy()方法,用于字典的浅复制(shallow copy)。与浅复制相对应的,还有深复制(deep copy)的概念。我们通过实例来理解一下二者的区别。浅复制和深复制的概念在很多编程语言中都涉及,大家应该理解并举一反三。

# file: ./5/5_10.py

# 深复制和浅复制
dict_1 = {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}
dict_2 = dict_1.copy()

print('dict_1:', dict_1)
print('dict_2:', dict_2)

# 修改value
print('modify 90->98'.center(64, '-'))
dict_1['score']['yuwen'] = 98
print('dict_1:', dict_1)
print('dict_2:', dict_2)

# 深拷贝
print('deep copy'.center(64, '-'))

import copy
dict_3 = copy.deepcopy(dict_1)
print('dict_1:', dict_1)
print('dict_3:', dict_3)
print('modify 98->99'.center(64, '-'))
dict_1['score']['yuwen'] = 99
print('dict_1:', dict_1)
print('dict_3:', dict_3)

输出结果为:

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}

dict_2: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}

-------------------------modify 90->98--------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

dict_2: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

---------------------------deep copy----------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

dict_3: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

-------------------------modify 98->99--------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 99, 'shuxue': 100}}

dict_3: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

 

所谓浅复制,其实是复制了内存的地址,并没有拷贝内存里面存的数据。

而深复制,则是重新分配了一块内存,并把数据拷贝进去。

所以,我们通过上面的实例可以看到,在浅复制的情况下,我们改变dict_1的值,dict_2依然会跟着变化,说明他们指向了同一块内存空间。而深复制的情况下,则互不影响,说明他们指向的是不同的内存空间。

通过下面的图,更加容易理解:

 

大家可以思考一下,为什么key为‘name’和‘age’的值不受浅复制的影响呢?

下一节我们讲集合Set的概念。


本节课程的视频和实例源码下载方式:点击->我的主页,查看个人简介。

我尽量坚持每日更新一节。

猜你喜欢

转载自blog.csdn.net/j00105840/article/details/106025357