Python_6_内置结构-字典

1. 字典介绍

  在 Python 中字典属于一种映射类型,它和 set 相同,同样属于非线性结构,Python 官方对 dict 有如下解释:

  • 一个映射对象映射一个可 hash 的值到任意一个对象上去。
  • 映射是可变的对象。
  • dict 是当前唯一一个标准的映射类型。
  • 字典的键几乎可以是任意的值。
  • 字典的值不必可 hash,也就是说值可以是列表,字典,或者其他任意可变的对象
  • 如果 key 相同,那么后面的 value 将会覆盖先前的 value
  • 不建议使用 float 类型作为字典的 key

  简单来说:字典是由 key:value 键值对组成的数据的集合,它的主要特点是 可变的无序的不重复的

字典的 key 必须是可 hash 对象,之所以快,因为其本质上是 用空间换时间

2. 字典的基本操作

  字典是除集合 (set) 以外另一种可变的非线性容器模型,在 Python 中非常强大,适合各种结构数据的存储、嵌套等。

2.1. 字典的定义

  字典的每个 key 与 value 的键值对用冒号 : 分割,每对之间用逗号 , 分割,整个字典包括在花括号 { } 中。例:{'a':1, 'b':2},Python 提供了多种创建字典的方式,如下:

  • 基本格式:d = dict() 或者 d = {}
  • dict(**kwargs):使用 name=value 对,来初始化一个字典
  • dict(iterable, **kwargs):使用可迭代对象和 name=value 构造字典,注意 可迭代对象必须是一个二元结构
  • dict(mapping, **kwargs): 使用一个字典构造另一个字典
  • dic = {'a':1, 'b':2, 'c':3, 'd':[1,2,3]}
  • dic = dict.fromkeys(iterable, value): 使用可迭代对象的值作为 key 生成字典,value 默认为 0,否则用指定的 value 对字典进行初始化。
In : d1=dict()

In : d2={}

In : d3 = dict(a=1, b=2) ; d3 
Out: {'a': 1, 'b': 2}

In : d4 = dict([('a', 1),('b', 2)], c=3, d=4) ; d4
Out: {'a': 1, 'b': 2, 'c': 3, 'd': 4}

In : d5 = dict(d4, e=5, f=6) ; d5
Out: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}

In : d7 = dict.fromkeys(range(5)) ; d7
Out: {0: None, 1: None, 2: None, 3: None, 4: None}

In : d8 = dict.fromkeys(range(5), 100) ; d8 
Out: {0: 100, 1: 100, 2: 100, 3: 100, 4: 100} 

2.2. 字典元素的访问

  有如下三种方式访问字典的键值对:

  1. d[key]:返回 key 对应的 value,key 不存在抛出 KeyError 异常
  2. dict.get(key[, default]):返回 key 对应的 value,key 不存在返回缺省值,如果没有设置缺省值返回 None
  3. dict.setdefault(key[, default]):返回 key 对应的值 value,key 不存在,添加 key:value 键值对(value 设置为 default),并返回 value,如果 default 没有设置缺省为 None
In : dic = {'a':1, 'b':2, 'c':3, 'd':4}

In : dic['a']
Out: 1

In : dic['e']   # 不存在 'e' 这个 key,所以直接访问会出现异常
---------------------------------------------------------------------------
KeyError                                  Traceback(most recent call last)
<ipython-input-34-87d22c709971> in <module>
----> 1 dic['e']

KeyError: 'e'

In : dic.get('a')     # key 存在,则返回对应的值
Out: 1

In : dic.get('e')     # 不存在,默认会返回 None,ipython 优化了 None 的输出,所以这里无显示

In : dic.get('e', 'not exist')     # 不存在时,由指定的 default 进行返回
Out: 'not exist'

In : dic.setdefault('a', 'ok')   # 设置 a 的值为 ok,a 存在,所以返回 a 对应的值 1
Out: 1

In : dic.setdefault('e', 'ok')   # 设置 e 的值为 ok,e 不存在,所以设置并返回 value(key 不存在时等同于设置并访问了)
Out: 'ok'

In : dic
Out: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 'ok'}

当然字典也可以被迭代访问,后面介绍

2.3. 字典的增删改

  由于字典可变、非线性、无序的特性,而且字典的 key 必须是可 hash 的对象,查找某个 key 也是直接 hash(),然后找到对应的位置的,所以我们对它某个 key 的修改可以理解为是时间复杂度 O(1) 的操作,效率很高,主要有以下几种方法。

  • d[key] = value: 将 key 对应的值修改为 value,key 不存在添加新的 key:value 对。
  • dic.update([other]) --> None: 使用另一个字典的 k,v 对更新本字典,key 不存在时添加,存在时则覆盖,所以不会返回新的字典,属于原地修改。
  • dic.pop(key[, default]): key 存在,移除它,并返回它的 value,key 不存在返回指定的 default,如果 default 未指定,那么会返回 KeyError 异常。
  • dic.popitem(): 移除并返回一个任意的键值对,字典为 empty 时,抛出 KeyError 异常。
  • dic.clear(): 清空字典。
  • del dic['a']: 通用的删除变量方法。
In : dic = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 'ok'}

In : dic['a'] = 1000 ; dic
Out: {'a': 1000, 'b': 2, 'c': 3, 'd': 4, 'e': 'ok'}

In : dic2 = {'a':200,'f':50} ; dic.update(dic2) ; dic
Out: {'a': 200, 'b': 2, 'c': 3, 'd': 4, 'e': 'ok', 'f': 50}

In : dic.pop('a')             # 弹出一个 key'a',key 存在返回 key 对应的 value
Out: 200

In : dic
Out: {'b': 2, 'c': 3, 'd': 4, 'e': 'ok', 'f': 50}

In : dic.pop('g')              # 弹出一个 key'g',key 不存在,又没有指定 default,则会报 KeyError 异常
---------------------------------------------------------------------------
KeyError                                  Traceback(most recent call last)
<ipython-input-49-311c3ba80251> in <module>
----> 1 dic.pop('g')

KeyError: 'g'

In : dic.pop('g','not exist')       # 指定了 default,当 key 不存在时,会返回指定的 default 值 
Out: 'not exist'

In : dic.popitem()          # 弹出一个 key:valyue 键值对,返回对象是元组形式
Out:('d', 4)

In : dic
Out: {'b': 2, 'c': 3, 'e': 'ok', 'f': 50}

In : del dic['e'] ; dic
Out: {'b': 2, 'c': 3, 'f': 50}

In : dic.clear()

In : dic
Out: {}

  当我们以字典的某个对象比如 keys,values,items 等为条件对字典进行遍历时,我们不能在遍历的同时删除字典的元素,字典在运行时不允许长度改变,但是在 list 中这种操作是可以的,但是会得到意想不到的结果,建议对容器进行遍历的同时不要修改它的长度。

In : s = {'a': 1, 'b': 2, 'c': 4, 'd': 5, 'e': 7, 'j': 10}

In : len(s)  
Out: 6

In : for i in range(6): 
...:     s.popitem() 
...:

In : s  
Out: {}

# 下面这种方式是错的,也是绝对不可以的。
In : for i in s.keys():
...:     s.pop(i)
...:
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-35-9c51f70f750a> in <module>
----> 1 for i in s.keys():
      2     s.pop(i)
      3

RuntimeError: dictionary changed size during iteration

3. 字典遍历

  在 Python 中,我们所说的基本数据结构:字符串、元组、列表,集合,包括字典,都可以认为是一个容器箱子,只要是容器,我们就可以进行遍历(是否有序和是否可以遍历没有必然关系,只不过有序的话是顺序拿出,而无序则是随机拿出),我们可以使用多种方式对字典进行迭代遍历,但是有些地方和其他类型不同。

3.1. 遍历字典的 key

  dic.keys(): --> dict_keys --> 返回字典 dic 的所有 key 组成的一个 dict_keys 视图集合(类 set 结构,不会生成新的内存空间)。

In : dic = {'a':1, 'b':2, 'c':3, 'd':4}

In : for key in dic: 
...:     print(key)
...:
a
d
c
b

In : for key in dic.keys(): 
...:     print(key)
...:
a
d
c
b

In : dic.keys()
Out: dict_keys(['a', 'd', 'c', 'b'])

  迭代字典就是在迭代字典的 key,所以直接迭代字典和使用字典的 keys() 方法返回一个 keys 的视图然后再迭代,是一样的效果。

3.2. 遍历字典的 value

  dic.values(): --> dict_values --> 返回字典 dic 的所有 values 组成的一个 dict_values 视图集合(类 set 结构,不会生成新的内存空间)。

In : for i in dic: 
...:     print(dic[i])
...:
1
2
3
4

In : for i in dic.values(): 
...:     print(i)
...:
1
2
3
4

In : dic.values()  
Out: dict_values([1, 2, 3, 4])

  可以首先遍历字典的 key,然后再通过 key 来访问对应的 value,也可以通过 dic.values() 直接访问 values。

3.3. 变量字典的键值对

  dic.items(): --> dict_items :返回字典 dic 的所有的 key 和 value(每个 key 和 value 的键值对由元组表示) 组成的一个 dict_items 视图集合(类 set 结构,不会生成新的内存空间)。

In : for i in dic.items(): 
...:     print(i) 
...:
('a', 1)
('b', 2)
('c', 3)
('d', 4)

In : for key,value in dic.items(): 
...:     print('key:{} value:{}'.format(key,value)) 
...:
key:a value:1
key:b value:2
key:c value:3
key:d value:4

  由于返回的每个键值对为元组格式,那么利用我们前面学的封装与解构,可以很方便的获取 key 和它对应的 value

3.4. 字典遍历小结

  在 Python3 中,keys、values、items 方法返回一个类似生成器的可迭代对象,不会把函数的返回结果复制到内存空间中。

  • Dictionary view 对象,可以使用 len()、iter()、in 操作
  • 字典的 entry 的动态视图,字典变化,视图将反映出这些变化
  • keys 返回一个类似 set 的对象,也可以看作是一个 set 集合,如果 values 可 hash 的话,那么 items 也可以看作是类 set 对象

  Python 2 中,keys、values、items 方法会返回一个新的列表,占据新的内存空间,所以 Python 2 建议使用 iterkeys、itervalues、iteritems,返回一个迭代器,而不是返回一个 copy

[11:20:32 python@centos7 ~]$ python
Python 2.7.5(default, Oct 30 2018, 23:45:53) 
[GCC 4.8.5 20150623(Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> s = {'a':1,'b':2}
>>> s.keys()    # 直接生成一个 keys 的列表
['a', 'b']
>>> s.iterkeys()   # 对象,迭代可以获取数据
<dictionary-keyiterator object at 0x7f271c5fa520>

4. defaultdict 默认值字典

  defaultdit object 是 dict 的子类,我们称它为默认值字典,即在创建字典时给所有的 value 指定一个默认值,它存放在 collections 模块中,使用前需要先进行导入。为什么有默认值字典这种类型呢?请看如下代码:

dic = {}
for i in 'abacdabeddfef':
    if i not in dic:     # 这句其实也可以优化为 dic[i] = dic.get(i, 0) + 1
        dic[i] = 0
    dic[i] += 1
print(dic)

# 结果:
{'a': 3, 'b': 2, 'c': 1, 'd': 3, 'e': 2, 'f': 2}

  我们在计算一个字符串或者一个列表中的元素重复的次数时,通常会用到字典对齐进行计数,如果元素不存在字典中,那么就需要初始化元素,当我们使用默认值字典时,就可以优化的更简洁。
  当 key 不存在时,返回的是工厂函数的默认值,比如 list 对应 [ ],str 对应的是空字符串,set 对应 set( ),int 对应 0

from collections import defaultdict
dic = defaultdict(int)   # defaultdict(lambda :0)  这种方法也可以设置默认为 0
for i in 'abacdabeddfef':
    dic[i] += 1    # 默认是 int 型,可以直接加减
print(dic)

# 结果:
defaultdict(<class 'int'>, {'a': 3, 'b': 2, 'c': 1, 'd': 3, 'e': 2, 'f': 2})

5. OrderedDict 有序字典

  Ordered dictionaries 像一个有序字典,但是它记住的是插入元素的顺序。当我们迭代有序字典时,它会按照这些键值对插入的顺序返回。它同样存在于 collections 模块中,需要使用时请先导入。

In : from collections import OrderedDict

In : dic = OrderedDict()  

In : dic = dic.fromkeys('abc', 1) ; dic
Out: OrderedDict([('a', 1),('b', 1),('c', 1)])

In : for k,v in dic.items():     # 按照插入的顺序
...:     print(k, v) 
...:
a 1
b 1
c 1

In : dic = dict([('a', 1),('b', 1),('c', 1)])  

In : for k,v in dic.items():    # 无序的
...:     print(k, v) 
...:
a 1
c 1
b 1

  注意:在 3.6 的版本的 python/ipython 解释器中,直接迭代或者打印时,是有序的(OrderedDict),但是在 3.5 版本以前都是随机的,千万不要以为字典是有序的!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容