轻松初探 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 条评论
登录 后参与评论

相关文章

来自专栏青玉伏案

代码重构(四):条件表达式重构规则

继续更新有关重构的博客,前三篇是关于类、函数和数据的重构的博客,内容还算比较充实吧。今天继续更新,本篇博客的主题是关于条件表达式的重构规则。有时候在实现比较复杂...

21090
来自专栏算法channel

程序员必知的算法和数据结构:2500字性能总结

以下5个步骤总结了此方法,依次为如下,我们设计的实验必须是可以重现的,我们形成的假设必须是具有真伪的。

8100
来自专栏小詹同学

【记录帖】从零打卡刷Leetcode——No.007

小詹此记录贴的读者越来越少了,也许是小詹总结的不够好欢迎留言区提出宝贵的意见!也欢迎和小詹一起定期刷leetcode,每周一和周五更新一题,每一题都吃透,欢迎一...

14230
来自专栏韩伟的专栏

基于对象和面向对象

“基于对象”是“面向对象”一次动态化变迁,它依赖于现代语言的动态特性,让方法和属性统一起来;用组合取代继承;以函数对象查找取代多态的方法调用。这些变化让面向对象...

1.5K00
来自专栏算法修养

接口和多态性

如果你又加了一个百度外卖,那么eat函数中又要new 一个BaiDu() ,给开发带来麻烦。我们希望的是,如果代码要扩展了,那么代码要尽最大可能的进行很小的改动...

9930
来自专栏tkokof 的技术,小趣及杂念

位运算实用指南

说明 : 想来这应该是初次接触移位操作符时一定会了解到的知识点,根据2进制的整数表示方法应该不难理解,原因细节不再赘述~

7310
来自专栏苦逼的码农

从零打卡leetcode之day 1--两数之和

不过心里才两个循环时间复杂度可是n的平方,心想肯定得超时,不过还是大胆提交一下提交,呵呵,居然通过了。。。。

13410
来自专栏AI派

Numpy 修炼之道 (13)—— 将python函数向量化

想要实现将python函数向量化,Numpy中的vectorize 和frompyfunc函数都可以满足要求。

46570
来自专栏机器学习算法工程师

一道网易笔试题引发的血案……

作者:柳行刚 编辑:黄俊嘉 网易的2016年笔试题,题目经典。 所以特地找来给各位有兴趣的童鞋看看, 有详细的解题思路以及代码喔~ 各位,请看题! 题目描述: ...

482120
来自专栏web前端教室

【视频】- 5分钟学习<函数式编程>

温馨提示:视频请点此观看 // 视频文字版: JavaScript 函数式编程是一个存在了很久的话题, 现在ES6语法对于函数式编程更为友好,所以开始变的更...

25660

扫码关注云+社区

领取腾讯云代金券