说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的内置函数 dict() 将创建一个新的字典,dict 对象是一个字典类。它是 Python 的内置映射类。映射一种支持任意键查找并实现了 Mapping 或 MutableMapping 抽象基类 中所规定方法的容器对象。
以下是用 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})
f = dict({'one': 1, 'three': 3}, two=2)
a == b == c == d == e == f
# True
更多快速使用可访问 Python 字典。
一些概念名称:
映射类型(Mapping Types)是一种关联式的容器类型,它存储了对象与对象之间的映射关系。 技术上,它是一种支持任意键查找并实现了 Mapping 或 MutableMapping 抽象基类 中所规定方法的容器对象。 此类对象的例子包括 dict, collections.defaultdict
, collections.OrderedDict
以及 collections.Counter
。
字典(dict)是 Python 中标准的映射类型,它是存储了一个个键值对(由键映射到值)的关联容器。 其中,键(key)必须是可哈希的Python 对象,而值(value)可以是任何 Python对象。
数字类型用作键时遵循数字比较的一般规则:如果两个数值相等 (例如 1 和 1.0) 则两者可以被用来索引同一字典条目。 (但是请注意,由于计算机对于浮点数存储的只是近似值,因此将其用作字典键是不明智的。)
字典可以通过将以逗号分隔的 键: 值 对列表包含于花括号之内来创建,例如: {'jack': 4098, 'sjoerd': 4127} 或 {4098: 'jack', 4127: 'sjoerd'},也可以通过 dict 构造器来创建。
一个对象的哈希值如果在其生命周期内绝不改变,就被称为 可哈希 (它需要具有 __hash__()
方法),并可以同其他对象进行比较(它需要具有 __eq__()
方法)。可哈希对象必须具有相同的哈希值比较结果才会相同。
字典可以用 dict 构造器返回一个新的字典,对照上文快速入门的示例,它的语法有以下几种:
class dict(**kwargs)
class dict(mapping, **kwargs)
class dict(iterable, **kwargs)
返回一个新的字典,基于可选的位置参数和可能为空的关键字参数集来初始化。
有以下几种方法:
键: 值
对的方式: {'a': 111, 'b': 222}
或者 {111: 'a', 222: 'b'}
{}
或者 {x: x ** 2 for x in range(10)}
dict()
、列表 dict([('a', aaa), ('b', 222)])
、关键字参数dict(a=111, b=222)
位置参数:
关键字参数:
相同键:
注意:
这些是字典所支持的操作(因而自定义的映射类型也应当支持):
操作 | 说明 |
---|---|
class dict(other) | 创建字典(other可以是字典、(key, value)对的迭代器或关键字参数) |
dict.fromkeys(seq[, value]) | 创建字典:用序列seq中的元素作为键,值全为value(未指定,则默认为None) |
len(d) | 返回字典d的长度(即d中元素的个数) |
reversed(d) | 返回字典反转迭代器,按键的顺序反转(字典现已保留顺序)3.8 新版功能 |
d[key] | 如果键key在字典d中,则返回其中key对应的值;否则抛出KeyError异常 |
d[key] = value | 设置d[key]的值为value(存在则修改,不存在则添加) |
del d[key] | 如果键key在字典d中,则从字典d中删除d[key];否则抛出KeyError异常 |
key in d | 如果key在字典d中,返回True;否则,返回False |
key not in d | 如果key在字典d中,返回False;否则,返回True |
iter(d) | 同iterkeys() |
d.clear() | 删除字典d中的所有元素 |
d.copy() | 返回字典d的浅拷贝 |
d.get(key[, default]) | 如果key在字典d中,则返回d[key];否则返回default(未指定,则默认为None) |
d.has_key(key) | 同key in d(推荐使用key in d) |
d.pop(key[, default]) | 如果key在字典d中,则返回并删除d[key];否则返回default(未指定,则抛出KeyError异常) |
d.popitem() | 返回并删除字典d中的任意一个元素(如果d为空,则抛出KeyError异常) |
d.setdefault(key[, default]) | 如果key在字典d中,则返回d[key];否则执行d[key] = default,并返回default(未指定,则默认为None) |
d.update([other]) | 将other中的(key, value)对添加到字典d中(other可以是字典、(key, value)对的迭代器或关键字参数) |
d.items() | 返回字典d的元素(key, value)对视图 |
d.keys() | 返回字典d的键视图 |
d.values() | 返回包含字典d中的值视图 |
示例如下:
# 创建字典
a = {'one': 1, 'two': 2, 'three': 3}
b = dict(one=1, two=2, three=3)
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict({'three': 3, 'one': 1, 'two': 2})
# 以上方法等效
a == b == c == d
# True
# 元素、键、值视图
d.items()
# dict_items([('three', 3), ('one', 1), ('two', 2)])
d.keys()
# dict_keys(['three', 'one', 'two'])
d.values()
# dict_values([3, 1, 2])
# 从列表中创建健为元素的字典
d = dict.fromkeys(['a', 'b', 'c'])
d
# {'a': None, 'c': None, 'b': None}
# 给出默认值
d = dict.fromkeys(['a', 'b', 'c'], 6)
d
# {'a': 6, 'c': 6, 'b': 6}
len(d) # 字典长度
# 3
# 顺序反转
reversed(d) # 字典反转迭代器
# <dict_reversekeyiterator at 0x7faf5198b510>
[*reversed(d)]
# ['two', 'one', 'three']
# 清除字典
d.clear()
d
# {}
# 浅拷贝
d = a.copy()
d
# {'one': 1, 'three': 3, 'two': 2}
# 返回指定键的值
d['three']
3
# 设置值
d['four'] = 4
d
# {'four': 4, 'one': 1, 'three': 3, 'two': 2}
# 删除项
del d['one']
d
# {'four': 4, 'three': 3, 'two': 2}
# 键是否(不)存在
'four' in d, 'four' not in d
# (True, False)
# 检测键
d.has_key('four')
# True
# 返回键的值
d.get('one'), d.get('one', 10)
# (None, 10)
# 带默认值增加项,存在
d.setdefault('two')
# 2
d
# {'four': 4, 'three': 3, 'two': 2}
# 不存在,给出默认值
d.setdefault('one', 1)
# 1
d
# {'four': 4, 'one': 1, 'three': 3, 'two': 2}
# 更新值
d.update(five=1)
d
# {'four': 4, 'one': 1, 'five': 1, 'three': 3, 'two': 2}
# 方法 2
d.update({'six': 6})
d
# {'four': 4, 'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
# 删除项
d.pop('four')
# 4
d
# {'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
d.popitem()
# ('five', 1)
d
# {'two': 2, 'six': 6, 'three': 3, 'one': 1}
要注意的是:
pop(key[, default])
和 popitem()
的区别在于:pop(key[, default])
如果 key 存在于字典中则将其移除并返回其值,否则返回 default。 如果 default 未给出且 key 不存在于字典中,则会引发 KeyError。popitem()
从字典中移除并返回一个 (键, 值) 对。 键值对会按 LIFO 的顺序被返回。popitem() 适用于对字典进行消耗性的迭代,这在集合算法中经常被使用。 如果字典为空,调用 popitem() 将引发 KeyError。在 3.7 版更改: 现在会确保采用 LIFO(后进先出)顺序。 在之前的版本中,popitem() 会返回一个任意的键/值对。reversed(d)
返回一个逆序获取字典键的迭代器。 这是 reversed(d.keys()) 的快捷方式。3.8 新版功能。如果字典的子类定义了方法 __missing__()
并且 key 不存在,则 d[key] 操作将调用该方法并附带键 key 作为参数。 d[key] 随后将返回或引发 __missing__(key)
调用所返回或引发的任何对象或异常。 没有其他操作或方法会发起调用 __missing__()
。 如果未定义 __missing__()
,则会引发 KeyError。 __missing__()
必须是一个方法;它不能是一个实例变量:
class Counter(dict):
def __missing__(self, key):
return 0
c = Counter()
c['red']
# 0
c['red'] += 1
c['red']
# 1
上面的例子显示了 collections.Counter 实现的部分代码。 还有另一个不同的 __missing__
方法是由 collections.defaultdict 所使用的。
Python 2.7 版本开始,引入了字典视图(Dictionary views)。字典视图 是字典的 动态视图:它们会与字典保持同步,实时反应出字典的变化。
字典视图共有 3 种:
该对象提供字典条目的一个动态视图,这意味着当字典改变时,视图也会相应改变。
d = {'three': 3, 'one': 1, 'two': 2}
# 元素、键、值视图
d.items()
# dict_items([('three', 3), ('one', 1), ('two', 2)])
d.keys()
# dict_keys(['three', 'one', 'two'])
d.values()
# dict_values([3, 1, 2])
两个 dict.values() 视图之间的相等性比较将总是返回 False。 这在 dict.values() 与其自身比较时也同样适用:
d = {'a': 1111}
d.values() == d.values()
# False
视图支持的方法:
d = {'a': 111, 'b':222, 'c': 333}
dictview = d.items()
dictview
# dict_items([('a', 111), ('b', 222), ('c', 333)])
len(dictview)
# 3
# 迭代器
iter(dictview)
# <dict_valueiterator at 0x7faf514558f0>
[*iter(dictview)]
# [('a', 111), ('b', 222), ('c', 333)]
# 成员检测
('a', 111) in dictview
# True
# 反转,产生一个反转迭代器
reversed(dictview)
# <dict_reverseitemiterator at 0x7faf5146d8f0>
[*reversed(dictview)]
# [('c', 333), ('b', 222), ('a', 111)]
# 返回 types.MappingProxyType 对象,
# 封装了字典视图指向的原始字典
dictview.mapping
# mappingproxy({'a': 111, 'b': 222, 'c': 333})
dictview.mapping.items()
# dict_items([('a', 111), ('b', 222), ('c', 333)])
dictview.mapping.keys()
# dict_keys(['a', 'b', 'c'])
dictview.mapping.values()
# dict_values([111, 222, 333])
键视图类似于集合,因为其条目不重复且可哈希。 如果所有值都是可哈希的,即 (键, 值) 对也是不重复且可哈希的,那么条目视图也会类似于集合。 (值视图则不被视为类似于集合,因其条目通常都是有重复的。) 对于类似于集合的视图,为抽象基类 collections.abc.Set 所定义的全部操作都是有效的 (例如 ==, < 或 ^)。
注意:
[]
切片操作一个使用字典视图的官网示例:
dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
keys = dishes.keys()
values = dishes.values()
# 迭代
n = 0
for val in values:
n += val
print(n)
# 504
# 键和值以相同的顺序(插入顺序)迭代
list(keys)
# ['eggs', 'sausage', 'bacon', 'spam']
list(values)
# [2, 1, 1, 500]
# 视图对象是动态的,实时反映 dict 更改
del dishes['eggs']
del dishes['sausage']
list(keys)
# ['bacon', 'spam']
# 集合操作
keys & {'eggs', 'bacon', 'salad'} # 求交集
# {'bacon'}
keys ^ {'sausage', 'juice'} # 求对称差集
# {'juice', 'sausage', 'bacon', 'spam'}
# 获取原始词典的只读代理
values.mapping
# mappingproxy({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})
values.mapping['spam']
# 500
字典和键视图支持数学上的集合操作:
| 操作 | 说明 |
|:-------------|:---------|
| dict & other
| 求交集 |
| dict | other
| 求并集 |
| dict - other
| 求差集 |
| dict ^ other
| 求对称差集 |
可以用以下方法合并字典,
d | other
合并 d 和 other 中的键和值来创建一个新的字典,两者必须都是字典。当 d 和 other 有相同键时, other 的值优先。Python 3.9 新版功能。
d |= other
类似于赋值的 += 操作,用 other 的键和值更新字典 d ,other 可以是 mapping 或 iterable 的键值对。当 d 和 other 有相同键时, other 的值优先。Python 3.9 新版功能。
通过字典视图可以随意对字典内容进行迭代,视图及迭代如下:
d = dict({'three': 3, 'one': 1, 'two': 2})
# 迭代器
iter(d)
# <dict_keyiterator at 0x7faf506cfb50>
[*iter(d)]
# ['three', 'one', 'two']
for key in iter(d):
print(key)
# 元素、键、值视图
d.items()
# dict_items([('three', 3), ('one', 1), ('two', 2)])
for k, v in iter(d.items()):
print(k, v)
d.keys()
# dict_keys(['three', 'one', 'two'])
for key in iter(d):
print(key)
d.values()
# dict_values([3, 1, 2])
for value in iter(d):
print(value)
在 Python 3.7 版开始字典顺序会确保为插入顺序,此行为是自 3.6 版开始的 CPython 实现细节。因此,字典及字典视图都支持反转:
d = dict({'three': 3, 'one': 1, 'two': 2})
d
# {'three': 3, 'one': 1, 'two': 2}
# 顺序反转
reversed(d) # 字典反转迭代器
# <dict_reversekeyiterator at 0x7faf5198b510>
[*reversed(d)] # 顺序进行了反转
# ['two', 'one', 'three']
字典和字典视图都是可逆的。在 3.8 版更改: 字典现在是可逆的。
从概念上讲,字典提供了这样一种抽象:容器中的元素之间完全独立(于是也没有先后顺序),“键”是访问元素的唯一方式。在这种 抽象层面 上,字典是 无序 的。
从实现上讲,字典其实是由 哈希表 实现的。而哈希表的基本思想是:通过 哈希函数(hash function)将“键”转换为“索引”,再使用“索引”去访问 连续列表(如C中的数组)中的元素。由此可知,在哈希表中:一方面,元素本质上是存储在一个连续列表中的,因此是 有序 的;另一方面,用户无法确定元素在连续列表中的实际位置(只能使用“键”去访问元素,而“键”与“索引”的映射关系是由哈希函数在内部指定的),因此又是 无序 的。
因此在 实现层面 上,字典同时具备了 无序 和 有序 的特点:
字典的键具有以下特性:
1)可哈希的(hashable)
只有 可哈希的 对象才能作为字典的键,一个可哈希的对象必须满足以下两个条件:
__hash__()
方法)__eq__()
方法)Python 中可哈希的对象有:
__hash__()
来修改默认行为)2)哈希等价键
假设有字典d的两个键:keyA和keyB,我们称 keyA 和 keyB 是 哈希相等,满足 hash(keyA) == hash(keyB)
。
如果keyA和keyB是哈希相等,那么它们将被视为完全相同的两个键,于是d[keyA]和d[keyB]会指向同一个字典元素。
例如,1和1.0就满足上述两个条件,因此是哈希等价键:
>>> hash(1), hash(1.0)
(1, 1)
>>> d = {}
>>> d[1] = 'int 1'
>>> d
{1: 'int 1'}
>>> d[1.0] = 'float 1'
>>> d
{1: 'float 1'}
以下是一些应用的示例。
对于一些逻辑分支功能,字典可以做路由,免去编写if else语句:
route = {True: 'case1', False: 'case2'} # 定义路由
route[7>6] # 'case1' 传入结果为布尔的变量、表达式、函数调用
# 定义计算方法
cal = {'+': lambda x,y: x+y, '*':lambda x,y: x*y}
# 使用
cal['*'](4,9) # 36
在格式化字符串时如果包含多个变量,可以将字典中的值用 key 在模板中占位传入:
# 字符串模板使用 key 占位
tpl = '我的名字叫%(name)s, 今年%(age)i岁了。'
info = {'name':'小明', 'age': 10}
# 字符串模板中用 key 传值
print(tpl % info)
# 我的名字叫小明, 今年10岁了。
对于变量较少的情况可以按顺序的方式提供变量,但对于变量较多的情况字典比较适合,同时模板清晰、可读性强。
以下有一个句子,需要对每个单词的数量进行统计,我们利用字典的形式进行存储,键为单词,值为数量:
# 句子
s = 'to be or not to be'
# 拆分为单词列表
s.split()
# ['to', 'be', 'or', 'not', 'to', 'be']
# 定义一个空字典
s_dict = {}
# 对单词列表进行迭代,对项的值加一
for i in s.split():
s_dict[i] = s_dict.get(i, 0) + 1
# 统计结果
s_dict
# {'to': 2, 'be': 2, 'or': 1, 'not': 1}
利用的是新值会替换同键的值特性,字典的 get 方法如果不存在此项时可给出默认值 0。以上操作我们可用字典推导式直接完成:
{i: s.split().count(i) for i in s.split()}
# {'to': 2, 'be': 2, 'or': 1, 'not': 1}
# 为了减少同单词的循环计算,我们可转为集合
{i: s.split().count(i) for i in set(s.split())}
records 是一个学生成绩表,我们需要计算这个成绩表中数学成绩的的平均分:
records = [
{'name': 'Tom', 'math': 88},
{'name': 'Lily', 'math': 77},
{'name': 'Xiaoming', 'math': 85},
{'name': 'Lilei', 'math': 90},
]
# 可以用列表推导式当成绩提取出来
[i.get('math') for i in records]
# [88, 77, 85, 90]
# 也可用生成器表达式生成一个迭代器
(i.get('math') for i in records)
# <generator object <genexpr> at 0x7f931edcaab0>
# 用 sum 计算迭代器里的数值
sum(i.get('math') for i in records)
# 除以数据个数,算出平均数
sum(i.get('math') for i in records)/len(records)
# 85.0
更新时间:2022-02-12 12:30:56 标签:python dict 字典