天天看点

初窥门径——字典的更好使用方式

字典推导式

列表推导式可能我们已经十分熟悉了,但是其实字典推导式也同样非常实用。

exam_list = [
    ['alibaba', 'china'],
    ['tencent', 'china'],
    ['orcale', 'america']
]
exam_dict = {compancy:country for compancy,country in exam_list}
exam_dict
           
{'alibaba': 'china', 'tencent': 'china', 'orcale': 'america'}
           

其实使用可以像列表推导一样灵活,如我们可以把公司名的前三个字母简称(并首字母大写)作为键,公司全名作为值。

{'Ali': 'alibaba', 'Ten': 'tencent', 'Orc': 'orcale'}
           

事实上你也很难在其他语言上找到比这更优雅的写法。

setdefault方法

我觉得这个方法是字典的精髓所在。下面看一个例子。

如我们要进行词云的计算操作,我们常规的做法是遍历一遍,求出结果。

for i in company_list:
    if i not in company_dict:
        company_dict[i] = 1
    else:
        company_dict[i] += 1 
company_dict
           
{'alibaba': 3, 'tencent': 1, 'orcale': 2}
           

这是我们的常规处理思路,但我觉得不够优雅。我们其实可以更简洁一点。

for i in company_list:
    new_company_dict.setdefault(i, []).append(1)
new_company_dict = {key:sum(value) for key, value in new_company_dict.items()}
new_company_dict
           

这样是不是更优雅一点,我也不能断言。但是第一种方法至少要进行三次键查询,而后者则规避了这些。

Counter类

事情上我们在进行上述的计算功能也不用那么麻烦,Counter类已经为我们做好了一切

from collections import Counter
new_com = Counter(company_list)
new_com
           
Counter({'alibaba': 3, 'tencent': 1, 'orcale': 2})
           

我们也可以把统计好的结果按着从小到大排列

创建有序字典

在说这句话时候,我们已经明确字典是无序的。其实在常规的理解中很多人认为字典是有序的。他的顺序就是添加到字典的时间顺序。为什么会这么认为呢,实际上在windows中字典里键值对的顺序往往就是添加到字典中的顺序。

但是在linux中往往不是那么回事,为什么python字典是无序的下面再讲。那么关于python创建有序字典可以参考我这篇博客。链接: python 把字典变为有序.

为什么字典的键必须是不可变对象

这个问题想必大家都考虑过,最开始我也想可能因为键值对是一一对应,不可变是为了达到这种对应关系。

可是如果两个列表[1,2,3] 和 [1,2,3,4] 这两个列表是不同的列表,这种不同的列表也无法作为字典的键但是元组却可以。

这种就比较奇怪了,其实字典是python高度优化的一个结构。因为其实有很多时候,字典会在python中有比较巧妙的运用。

python字典的键采用了散列表算法来实现的,所谓的散列表就是哈希表。,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做[散列函数,存放记录的数组叫做散列表。

在python字典中是这样实现高效查询的,为了获取 my_dict[search_key] 背后的值,Python 首先会调用 hash(search_key) 来计算 search_key 的散列值,在散列表里查找表元(具体取几位,得看 当前散列表的大小)。若找到的表元是空的,则抛出 KeyError 异 常。若不是空的,则表元里会有一对 found_key:found_value。 这时候 Python 会检验 search_key == found_key 是否为真,如 果它们相等的话,就会返回 found_value。

这也就是python字典的键必须是不可变的原因,因为要求他是可哈希的。

为什么顺序会打乱

其实不止dict用的散列表,set同样如此。dict 和 set 背后的散列表效率很高,对它的了解越深入,就越能理解 为什么被保存的元素会呈现出不同的顺序,以及已有的元素顺序会发生 变化的原因。同时,速度是以牺牲空间为代价而换来的。

随着我们学习的深入,我们发现db为了实现速度的提升建立索引。数仓为了实现数据的提升,使用分区。再到python中的字典和集合,都是牺牲空间来换时间。

正如那句话所说世上安得两全法。有得到必须得有牺牲。