Python字典(dict)使用技巧
字典dict是Python中使用频率非常高的数据结构,关于它的使用,也有许多的小技巧,掌握这些小技巧会让你高效地的使用dict,也会让你的代码更简洁.
1.默认值
假设name_for_userid存放的是name和id的映射关系:
name_for_userid = {
1: '张三',
2: '李四',
3: '王五',
}
获取name_for_userid中的某一个id的name,最简单的方式:
name_for_userid[1]
'张三'
这种方式虽然简单,但有一个不便之处就是,如果获取一个不存在于name_for_userid中的值程序会出现异常:
name_for_userid[4]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-138-66c7bf5cd53a> in <module>()
----> 1 name_for_userid[4] KeyError: 4
很多时候我们不希望程序出现这种异常,为了避免这种情况,可以在获取name之前先判断该id是否已经存在name_for_userid中:
if 4 in name_for_userid:
print(name_for_userid[4])
else:
print("Not Found")
"Not Found"
这种写法虽然可行,但是效率不高,因为获取某一个id对应的name最多需要查询两次:第一次是先判断该id是否存在name_for_userid中,若存在则第二次从name_for_userid中取出name值。还有一种写法:
try:
print(name_for_userid[4])
except KeyError:
print("Not Found")
"Not Found"
这种写法代码有些冗余,不够简洁,所以,dict提供了get方法,这个方法的好处就是可以设值default值,对于那些不存在于dict中的key会返回default值作为它的value:
name_for_userid.get(4,"None")
"None"
2.排序
card = {'a': 4, 'c': 2, 'b': 3, 'd': 1}
用sorted对card排序,其实是用card的key进行排序,如下:
sorted(card.items())
[('a', 4), ('b', 3), ('c', 2), ('d', 1)]
有些时候我们需要对card的value进行排序,这个时候就可以使用sorted函数中的key这个参数,我们可以help一下这个函数的用法:
help(sorted)
Help on built-in function sorted in module builtins:
sorted(iterable, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order. A custom key function can be supplied to customise the sort order, and the
reverse flag can be set to request the result in descending order.
所以我们可以自定义一个key函数sorted_by_value:
def sorted_by_value(item):
return item[1] sorted(card.items(),key=sorted_by_value)
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
其实,如果使用lambda匿名函数的话,代码会更简洁:
sorted(card.items(),key=lambda item:item[1])
如果让别人更轻松的理解你的意图,你可以尝试用下面的方法:
import operator
sorted(card.items(),key=operator.itemgetter(1))
需要注意的是,operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取该对象上指定域的值。
其实,还是比较喜欢lambda表达式,因为它灵活,简洁,比如,要对依据value的绝对值对dict排序,就可以这样写:
sorted(card.items(),key=lambda item:abs(item[1]))
3.switch...case
有句老话是这样说的,Python过去现在以及以后都不会有switch...case语句。这是因为if…elif…else和dict都能实现switch...case语句,所以switch...case就没有存在的必要了。
- if...elif...else
if cond == 'cond_a':
handle_a()
elif cond == 'cond_b':
handle_b()
else:
handle_default()
用if...elif...else来实现switch的功能,好处就是可读性强,但是如果处理的条件比较多,这样的写法就有些啰嗦和冗余。所以,这个时候dict就又闪亮登场了。
- dict
func_dict = {
'cond_a': handle_a,
'cond_b': handle_b
} cond = 'cond_a'
func_dict[cond]()
相对于if...elif...else,dict就显得清爽了许多,另外,如果想要实现default我们也可以使用dict的get()方法:
>>>func_dict.get(cond, handle_default)()
这样即使cond不在func_dict中,程序也不会异常中止。
下面再举一个例子,如下
>>>def dispatch_if(operator, x, y):
if operator == 'add':
return x + y
elif operator == 'sub':
return x - y
elif operator == 'mul':
return x * y
elif operator == 'div':
return x / y
>>>def dispatch_dict(operator, x, y):
return {
'add': lambda: x + y,
'sub': lambda: x - y,
'mul': lambda: x * y,
'div': lambda: x / y,
}.get(operator, lambda: None)()
如果想的再深入点的话,上面的例子就性能上来说不是最优的,因为,每次调用dispatch_dict函数的时候都会生成一个临时的包含各种操作码(加减乘除)的字典,最好的情况当然是这个字典只生成一次(常量),下次再调用此函数的时候,直接使用此字典就可以了,另外,python中的operator模块已经实现了加减乘除如:operator.mul, operator.div ,完全可以替代lambda表达式。这里只是举个例子以更好的明白if…elif…else和dict的异同。
4.合并dict的几种方法
有的时候需要用一个dict去更新另一个dict,比如,用户自定义的配置文件去覆盖默认配置,等等。假设有下面两个dict:
>>> xs = {'a': 1, 'b': 2}
>>> ys = {'b': 3, 'c': 4}
最常用的就是用dict内置的update()方法:
>>> zs = {}
>>> zs.update(xs)
>>> zs.update(ys)
>>> zs
{'a': 1, 'b': 3, 'c': 4}
还有一种方法就是使用内置的dict():
>>> zs = {**xs, **ys}
{'a': 1, 'b': 3, 'c': 4}
5.美观打印
当打印一些调试信息的时候,美观的打印输出有的时候能让人很直观的看出关键信息,提高调试的效率。
最朴素的打印:
>>> mapping = {'a': 23, 'b': 42, 'c': 0xc0ffee}
>>> str(mapping)
"{'c': 12648430, 'a': 23, 'b': 42}"
借助内置模块json可以实现更加直观的表现形式:
>>> import json
>>> json.dumps(mapping, indent=4, sort_keys=True)
{
"a": 23,
"b": 42,
"c": 12648430
}
但是这种方法有一定的限制:
>>> mapping['d'] = {1, 2, 3}
>>> json.dumps(mapping)
TypeError: "set([1, 2, 3]) is not JSON serializable" >>> json.dumps({all: 'yup'})
TypeError: "keys must be a string"
所以,还可以使用下面的方式:
>>> import pprint
>>> pprint.pprint(mapping)
{'a': 23, 'b': 42, 'c': 12648430, 'd': set([1, 2, 3])}
6.彩蛋
最后介绍几个关于dict比较有意思的东西 .
>>>print("keys:",list({True: 'yes', 1: 'no', 1.0: 'maybe'}.keys()))
>>>print("values:",list({True: 'yes', 1: 'no', 1.0: 'maybe'}.values()))
上面的代码会打印出什么结果呢,一开始我认为是下面的输出:
keys: [True,1,1.0]
values: ['yes','no','maybe']
真是too young too simple,其实结果应是:
keys: [True]
values: ['maybe']
why?再仔细想想的化,既然key是True,那value为什么不是yes而是maybe,再者,既然value是maybe,那key值为什么不是1.0而是True呢?你们的疑问是不是和我一样?
其实,当Python在处理dict表达式的时候,它会先根据dict类创建出dict的实例,然后按照item(key:value)在dict表达式出现的顺序依次赋值,如下:
>>>xs = dict()
>>>xs[True] = 'yes'
>>>xs[1] = 'no'
>>>xs[1.0] = 'maybe'
更奇怪的是,dict会用下面的表达式认为他的key值都是相等的:
>>>True == 1 == 1.0
True
看到1==1.0你也许不会感到奇怪,但是为什么会有True==1?当时看到这里我也有点懵逼的感觉.
翻阅了一下Python的官方文档,在这一章节The standard type hierarchy中发现下面这段话:
“The Boolean type is a subtype of the integer type, and Boolean values behave like the values 0 and 1, respectively, in almost all contexts, the exception being that when converted to a string, the strings ‘False’ or ‘True’ are returned, respectively.”
意思就是说,Boolean是Int的子类,就是0和1.那么我们可以验证一下:
>>>["No","Yes"][True]
结果果然是Yes,
>>>["No","Yes"][False]
结果也果然是No.但是为了清晰起见,不建议这样用.
看到这里你可能会问,这和dict有毛关系.接着往下看:
就Python而言,True,1和1.0都表示相同的字典key值,当解释器在执行dict表达式的时候,它会把后面的同一个key(True)的value值覆盖掉这个key(True)之前的value值.所以就会有下面的结果:
>>>{True: 'yes', 1: 'no', 1.0: 'maybe'}
{True: 'maybe'}
但是key为什么没有被最后的1.0覆盖呢?其实道理也很简单,既然是一样的key,为什么还要多此一举再用多余的时间去更新"相同的"key呢?这是出于CPython解释器性能的考虑.
>>>ys = {1.0: 'no'}
>>>ys[True] = 'yes'
>>>ys
输出:{1.0: 'yes'}
根据我们现在所了解到的,从表面上看是当key值相同时才会覆盖掉已有的value值,但是,事实证明,这不仅仅和判定相等的__eq__有关系.
Python的字典是通过哈希表实现的,也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数,存放记录的数组叫做哈希表。但是没有十分完美的哈希函数计算出来的存储位置都是不一样的,所以就会存在哈希冲突的情况,也就是不同的key计算出来的存储位置是同一个地址.下面来看看究竟是什么导致了更新value值.
- 先定义如下的一个类:
>>>class AlwaysEquals:
def __eq__(self, other):
return True
def __hash__(self):
return id(self)
AlwaysEquals有两个方法:__eq__和__hash__.
首先,由于__eq__返回True,所以下面的条件表达式都是True:
>>>AlwaysEquals() == AlwaysEquals()
>>>AlwaysEquals() == 42
>>>AlwaysEquals() == 'waaat?'
其次,由于__hash__返回该对象的id值,也就是内存地址,所以有:
>>>objects = [AlwaysEquals(),AlwaysEquals(),AlwaysEquals()]
>>>[hash(obj) for obj in objects]
[140388524604608, 140388524604664, 140388524604720]
所以,综上,当值相等而hash值不相等时候,是否会存在value值覆盖:
>>>{AlwaysEquals(): 'yes', AlwaysEquals(): 'no'}
结果是:
{<__main__.AlwaysEquals at 0x7faec023bbe0>: 'yes',<__main__.AlwaysEquals at 0x7faec023bac8>: 'no'}
- 重新定义一个类SameHash:
>>>class SameHash:
def __hash__(self):
return 1
>>>a = SameHash()
>>>b = SameHash()
由于__hash__返回1,所以有:
>>> a == b
False
>>> hash(a), hash(b)
(1, 1)
当值不相等而hash值相等的时候,是否会存在value值覆盖:
>>> {a: 'a', b: 'b'}
{ <SameHash instance at 0x7f7159020cb0>: 'a',<SameHash instance at 0x7f7159020cf8>: 'b' }
综上这两种情况,value值都不会被更新。
而{True: 'yes', 1: 'no', 1.0: 'maybe'} 会出现更新key所对应的value值,是因为:
>>> True == 1 == 1.0
True
>>> (hash(True), hash(1), hash(1.0))
(1, 1, 1)
他们同时满足条件表达式相等且hash值也相等。
Python字典(dict)使用技巧的更多相关文章
- Python字典增删操作技巧简述
Python编程语言是一款比较容易学习的计算机通用型语言.对于初学者来说,首先需要掌握的就是其中的一些基础应用.比如今天我们为大家介绍的Python字典的相关操作,就是我们在学习过程中需要熟练掌握的技 ...
- python字典dict的增、删、改、查操作
## python字典dict的增.删.改.查操作dict = {'age': 18, 'name': 'jin', 'sex': 'male', }#增# dict['heigh'] = 185 # ...
- Python 字典 dict() 函数
描述 Python 字典 dict() 函数用于创建一个新的字典,用法与 Pyhon 字典 update() 方法相似. 语法 dict() 函数函数语法: dict(key/value) 参数说明: ...
- 'dict_values' object does not support indexing, Python字典dict中由value查key
Python字典dict中由value查key 众所周知,字典dict最大的好处就是查找或插入的速度极快,并且不想列表list一样,随着key的增加越来越复杂.但是dict需要占用较大的内存空间,换句 ...
- python 字典dict - python基础入门(15)
前面的课程讲解了字符串str/列表list/元组tuple,还有最后一种比较重要的数据类型也需要介绍介绍,那就是python字典,俗称:dict. python中的字典可与字符串/列表/元组不同,因为 ...
- !!Python字典增删操作技巧简述+Python字典嵌套字典与排序
http://developer.51cto.com/art/201003/186006.htm Python编程语言是一款比较容易学习的计算机通用型语言.对于初学者来说,首先需要掌握的就是其中的一些 ...
- python 字典 dict 该注意的一些操作
在用python处理dict 的时候,有几个该注意的地方,这里跟大家提一下: 1)操作dict 时,尽量少产生新的列表对象.比如: 遍历dict的时候,如果用 dic = {"a" ...
- python 字典(dict)按键和值排序
python 字典(dict)的特点就是无序的,按照键(key)来提取相应值(value),如果我们需要字典按值排序的话,那可以用下面的方法来进行: 1 下面的是按照value的值从大到小的顺序来排序 ...
- python 字典dict和列表list的读取速度问题, range合并
python 字典和列表的读取速度问题 最近在进行基因组数据处理的时候,需要读取较大数据(2.7G)存入字典中,然后对被处理数据进行字典key值的匹配,在被处理文件中每次读取一行进行处理后查找是否在字 ...
随机推荐
- Phabricator API Go 创建task/提交文件到Phabricator
Go Phabricator API 代码/程序创建task/提交文件到Phabricator Creat Task or upload file to phabricator with code i ...
- 基于Xilinx FPGA的视频图像采集系统
本篇要分享的是基于Xilinx FPGA的视频图像采集系统,使用摄像头采集图像数据,并没有用到SDRAM/DDR.这个工程使用的是OV7670 30w像素摄像头,用双口RAM做存储,显示窗口为320x ...
- AtCoder Regular Contest 082
我都出了F了……结果并没有出E……atcoder让我差4分上橙是啥意思啊…… C - Together 题意:把每个数加1或减1或不变求最大众数. #include<cstdio> #in ...
- bzoj 2946
Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l 读入单词 l 计算最长公共子串的长度 l 输 ...
- BZOJ2282: [Sdoi2011]消防
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2282 答案一定是在直径上的一段,然后答案一定不会小于不在直径上的点到直径的距离(要是可以的话那 ...
- jquery dataTimePicker日历插件(精确到小时)
效果图: 下载地址:https://github.com/WangChangyao/jquery-dataTimePicker.git <!DOCTYPE html> <h ...
- 全面理解Java内存模型
尊重原创:http://blog.csdn.net/suifeng3051/article/details/52611310 Java内存模型即JavaMemory Model,简称JMM.JMM定义 ...
- 【转载】keil5中加入STM32F10X_HD,USE_STDPERIPH_DRIVER的原因
初学STM32,在RealView MDK 环境中使用STM32固件库建立工程时,初学者可能会遇到编译不通过的问题.出现如下警告或错误提示: warning: #223-D: function &qu ...
- GitHub上传文件不能超过100M的解决办法
http://blog.csdn.net/u010545480/article/details/52995794 上传项目到GitHub上,当某个文件大小超过100M时,就会上传失败,因为默认 ...
- 百度编辑器ueditor
,怎么将上传的图片路径改到项目的public/uploads文件夹呢?哪位大神改过