轻松初探 Python 篇(五)—dict 和 set 知识汇总

这是「AI 学习之路」的第 5 篇,「Python 学习」的第 5 篇

dict

dict 是 Python 内置的字典类型,熟悉 Java 的同学可以把它类比为 Map。dict 使用键值对来存储(key-value),它的查找速度特别快。

dict 一般用在什么场景呢?假设我们需要根据公司名字查找公司地址,按照我们之前的写法,我们需要先建立两个 list ,一个存储公司名字,一个存储公司总部地址,然后查找公司名字,记录好列表位置,再从地址列表查找到具体元素,你还得保证两个表元素位置必须一一对应。不仅如此,如果表很长,那遍历查找效率将会非常低。

>>> Inc = ['腾讯','阿里','百度']
>>> adress = ['深圳','杭州','北京']
>>> BaiduAdress = adress[Inc.index('百度')]
>>> BaiduAdress
'北京'

我们现在用 dict 实现,使用一个「公司-地址」这样的键值对来进行存储数据,查找的时候,我们只需要输入公司名字,就可以查找到对应的地址,同时,不论 dict 的数据有多少,查找单项的速度都是一样的,而且非常迅速。

>>> Inc_dict = {'腾讯':'深圳','阿里':'杭州','百度':'北京'}
>>> Inc_dict['百度']
'北京'

dict 速度这么快的原理就是使用了空间换取时间的方法,将无限集映射到一个有限集中。通过一个散列函数来计算每一个 key 应该存放在内存中的位置,然后把 value 存储在内存的这个位置上,等到需要取出 key 对应的 value 的时候,只需要通过函数计算出这个位置,然后直接去拿就行了。是不是有点像我们查字典的步骤呢?

通过散列函数求出的最终值就是对应的哈希值(Hash),Java 中的 Map 最常用的实现 HashMap 也是用类似的原理来设计的。Hash 算法也是数据结构中特别重要的一个知识点,所以如果我们计算机的基本功扎实,学哪门语言的时候都是融会贯通的

当然,散列函数本身比较复杂,还要牵扯到冲突的解决问题,简单来说,不同的 key 通过散列函数求得的内存位置可能是一样的,这样就导致了冲突,解决这种冲突的方法有很多,Python 设计者选择了开放定址法,在冲突的时候用另一个不同的函数再计算。在这里我就不深入讨论了,有兴趣的同学可以查阅下相关资料。

我们有很多种方式进行 dict 的初始化,下面几种初始化方式都会获得{"one": 1, "two": 2, "three": 3}

>>> a = dict(one=1,two=2,three=3)
>>> b = {'one':1,'two':2,'three':3}
>>> c = dict(zip(['one','two','three'],[1,2,3]))
>>> d = dict([('two',2),('one',1),('three',3)])
>>> e = dict({'three':3,'one':1,'two':2})
>>> a == b == c == d == e
True

除了通过初始化以外,还可以通过 key 来放入值,再次传入相同 key ,不同 value,将会覆盖前面传入的 value。如果某个 key 不存在,获取该 key 的 value 将会报 KeyError 错误。

>>> Inc_dict['途牛'] = '南京'
>>> Inc_dict['途牛']
'南京'
>>> Inc_dict['途牛'] = '金陵'
>>> Inc_dict['途牛']
'金陵'
>>> Inc_dict['小米']
KeyError:'小米'

为了防止获取 key 不存在的情况。我们可以用 in 来判断 dict 中是否已经存储过以这个 key 来存储的键值对。或者用 get() 方法来获取 value,如果 key 不存在,get() 将返回 None,可以设置一个参数来表示 key 不存在时候的默认返回值。

>>> '小米' in Inc_dict
False
>>> Inc_dict.get('小米')
>>> Inc_dict.get('小米','北京')
北京

通过 pop(key) 方法,来返回并删除对应的 value:

>>> Inc_dict.pop('腾讯')
'深圳'
>>> Inc_dict
{'阿里':'杭州','百度':'北京','途牛':'金陵'}

最后介绍下 dict 的迭代,我们知道 list 迭代可以简单的通过 for 来遍历,dict 迭代需要多做一些操作。

>>> d = {'a':1,'b':2,'c':3}
>>> for key in d:
...     print(key)
...
'a'
'c'
'b'

dict 默认的迭代方式是迭代 key ,如果你需要迭代 value 可以通过 d.values() 来获取 value 的列表

>>> for value in d.values()
...     print(value)
...
1
3
2

当然,你还可以同时迭代 key 和 value

>>> for k, v in d.items():
...     print(k, v)
...
a 1
c 3
b 2

细心的同学一定发现了迭代的顺序和我们初始化定义的顺序是不同的,之前也提到了,dict 内部存放顺序是根据散列函数决定的,所以最后的存放顺序不一定和插入顺序一致,那我们迭代顺序显然是不确定的了。

dict 的设计是典型的以空间换取时间,大家学习 Python 越深入就会发现 Python 的设计里有很多这样的设计, Python 设计的时候,大概已经不是内存最大就 4,500K 的年代了吧(手动嬉笑)。所以 dict 的特点就是,查找和插入的速度非常快,并且不随元素数量的增长而变慢

注意:key 必须是不可变对象(字符串,整数等),如果 key 是 list,就会报错 TypeError: unhashable type: 'list',tuple 虽然是不可变对象,但如果传入的 tuple 元素有可变对象,依然会报错。

>>> d = {'a':1}
>>> d = {'a':1,(1,):2}
>>> d = {'a':1,(1,):2,(1,[1]):3}
TypeError: unhashable type: 'list'

set

set 和 dict 很像,不过 set 不存储键值对,你可以把它想像成只存储 key 的 dict,也可以理解成数学中的无序无重复集合这个概念。所以在 set 中是没有重复元素的,也只能存放不可变元素。我们可以通过一个 list 来创建 set。同样,也是用大括号表示。

>>> s = set([1,2,3])
>>> s
{1,2,3}
>>> s = set([1,2,3,3,3])
>>> s
{1,2,3}

我们可以看到,重复的元素自动被过滤了,同时 set 也是无序的,虽然创建时候显示看起来好像是有序的。我们来看看 set 的一些常用方法。

>>> s.add(4)
>>> s
{1,2,3,4}
>>> s.add(4)
>>> s
{1,2,3,4}

add(key)添加元素到 set 中,但添加重复元素将不会生效

>>> s.remove(4)
>>> s
{1,2,3}
>>> s.remove(4)
KeyError: 4
>>> s1 = {1,2,3}
>>> s2 = {2,3,4}
>>> s1 & s2
{2,3}
>>> s1 | s2
{1,2,3,4}

remove(key)删除元素,如果 key 不存在会报错。同时,set 之前说过可以看成是集合,所以可以做一些交并集的操作。

原文发布于微信公众号 - WeaponZhi(WeaponZhi)

原文发表时间:2017-11-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

treeview 绑定文件夹和文件

451
来自专栏互联网开发者交流社区

STC-单片机控制系统

1113
来自专栏田超学前端

【微信小程序】c# 实现获取openid、session_key 服务端

5050
来自专栏我和未来有约会

Silverlight制作逐帧动画 v2 - part2

Silverlight制作逐帧动画 v2 - part2 在这里完善了一下算法,加入了fps的机制进去。 private string[] ...

1886
来自专栏技术之路

sqlserver 的事务和c#的事务

sql的事务 1 sql 2 create database model 3 go 4 use model 5 go 6 create table ...

1919
来自专栏大内老A

开发自己的Data Access Application Block[下篇]

上接:[原创] 我的ORM: 开发自己的Data Access Application Block - Part I 4. Database 下面来介绍重中之重...

2236
来自专栏跟着阿笨一起玩NET

使用延迟的FileSystemWatcher来避免重复触发事件

  程序里需要监视某个目录下的文件变化情况: 一旦目录中出现新文件或者旧的文件被覆盖,程序需要读取文件内容并进行处理;但在实际处理中发现当一个文件产生变化时,C...

912
来自专栏张善友的专栏

通过SmtpClient发送Exchange会议邮件

看到C#中调用Outlook API 发起会议 ,这个完全可以用SMTP方式实现的,下面我的项目中使用的代码: 对于.NET而言,从2.0开始,发邮件已经是一件...

1939
来自专栏c#开发者

about store RecordField submit emptystring issue

operate screenshot When click save button submit to change,trace store before...

3447
来自专栏Golang语言社区

GO语言 TCP传输实例

package main import ( "net" "fmt" ) var ( maxRead = 1100 msgStop = []byt...

3396

扫码关注云+社区