1.在Python中标准库中所有映射类型都是利用dict来实现的,因此它们有个共同的限制,即只有可散列 的数据类型才能用作这些映射的键,注意只有键有这个要求,值并不需要是可散列的数据类型。
在Python中原子不可变数据类型(str、bytes和数值类型)都是可散列类型,frozenset也是可散列类型,因为根据其定义,frozenset里只能容纳可散列类型。元组的话,只有当一个元组里包含的所有元素都是可散列类型的情况下,它才是可散列的。
一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们的id()函数的返回值。
2.字典的创建形式有如下几种方法:
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({'one': 1, 'two': 2, 'three': 3})
print(a==b==c==d==e)
当然还可以利用字典推导来创建新的字典
DIAL_CODES = [
(86, 'China'),
(91, "India"),
(1, "United States"),
(62, 'Indonesia'),
(55, 'Brazil'),
(92, 'Pakistan'),
(880, "Bangladesh"),
(234, "Nigeria"),
(7, 'Russia'),
(81, 'Japan'),
]
country_code = {country: code for code, country in DIAL_CODES}
print(country_code)
code_country = {code: country.upper() for country, code in country_code.items() if code < 66}
print(code_country)
3.常见的映射方法
| dict | defaultdict | OrederedDict | |
---|---|---|---|---|
d.clear() | √ | √ | √ | 移除所有元素 |
d.__contains__(k) | √ | √ | √ | 检查k是否在d中(k是键) |
d.copy() | √ | √ | √ | 浅复制 |
d.__copy__() |
| √ |
| 用于支持copy.copy |
d.default_factory |
| √ |
| 在 __missing__ 函数中被调用的函数,用以给未找到的元素这只值 |
d.__delitem__(k) | √ | √ | √ | del d[k] 移除键为k的元素 |
d.fromkeys(it, [initial]) | √ | √ | √ | 将迭代器it里的元素设置为映射里的键,如果有initial参数,就把它作为这些键对应的值。(默认是None) |
d.get(k, [default]) | √ | √ | √ | 返回键k对应的值,如果字典里没有键k,则返回None或者default |
d.__getitem__(k) | √ | √ | √ | 让字典d能用d[k] 的形式返回键k对应的值 |
d.items() | √ | √ | √ | 返回d里所有的键值对 |
d.__iter__() | √ | √ | √ | 获取键的迭代器 |
d.keys() | √ | √ | √ | 获取所有键 |
d.__len__() | √ | √ | √ | 可以用len(d) 的形式得到字典里键值对的数量 |
d.__missing__(k) |
| √ |
| 当 __getitem__ 找不到对应键的时候,这个方法会被调用 |
d.move_to_end(k, [last]) |
|
| √ | 把键为k的元素移动到最靠前或者最靠后的位置(last的默认值是True) |
d.pop(k, [default]) | √ | √ | √ | 返回键k所对应的的值,然后移除这个键值对。如果没有这个键,返回None或者default |
d.popitem() | √ | √ | √ | 随机返回一个键值对并从字典里移除它 |
d.__reversed__() |
|
| √ | 返回倒叙的键的迭代器 |
d.setdefault(k, [default]) | √ | √ | √ | 若字典里有键k,则直接返回k所对应的值;若无,则让d[k]=default,然后返回default |
d.__setitem__(k, v) | √ | √ | √ | 实现d[k]=v操作,把k对应的值设为v |
d.update(m, [**kargs]) | √ | √ | √ | m可以是映射或者键值对迭代器,用来更新d里对应的条目 |
d.values() | √ | √ | √ | 返回字典里的所有值 |
用setdefault处理找不到的键,示例如下:
"""创建一个从单词到其出现情况的映射"""
import sys
import re
WORD_RE = re.compile(r'\w+')
index = {}
with open("xx.txt", encoding='UTF-8') as fp:
# enumerate(sequence, [start=0]) sequence -- 一个序列、迭代器或其他支持迭代对象。start -- 下标起始位置。
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start() + 1
location = (line_no, column_no)
# 下面是一种不好的实现
occurrences = index.get(word, [])
occurrences.append(location)
index[word] = occurrences
# 上面三行等价于下面一行
# 获取单词的出现情况列表,如果单词不存在,把单词和一个空列表放进映射,然后返回这个空列表
# 这样就能在不进行第二次查找的情况下更新列表
index.setdefault(word, []).append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
print(word, index[word])
在上述的两种实现中,二者的效果都是一样的,只不过前者至少要进行两侧键查询-如果键不存在则是三次,用setdefault只需要一次就可以完成整个操作。
4.映射的弹性键查询
有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值。有两种方法可以帮我们达到这个目的,一个是通过defaultdict这个类型而不是普通的dict, 另一个是自己定义一个dict的子类,然后在子类中实现__missing__方法。
方法一如下:
import sys
import re
import collections
WORD_RE = re.compile(r'\w+')
# 把list构造方法作为default_factory来创建一个defaultdict
index = collections.defaultdict(list)
with open(sys.argv[1], encoding='UTF-8') as fp:
# enumerate(sequence, [start=0]) sequence -- 一个序列、迭代器或其他支持迭代对象。start -- 下标起始位置。
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start() + 1
location = (line_no, column_no)
# 如果index并没有word的记录,那么default_factory会被调用,为查询不到的键创造一个值。
# 这个值在这里是一个空列表,然后这个空列表会被赋值给index[word],继而被当做返回值返回
# 因此,append(location)操作总能成功
index[word].append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
print(word, index[word])
在创建defaultdict对象的时候,我们需要给它配置一个为找不到键创建默认值的方法。具体而言,在实例化一个defaultdict的时候,需要给构造方法提供一个可调用对象,这个可调用对象会在__getitem__碰到找不到的键的时候被调用,让 __getitem__返回一个默认值。
比如在上面,我们在实例化defaultdict的时候,提供了list可调用对象,当 __getitem__找不到键的时候,会按一下步骤来走:
1.调用list()来创建一个新列表
2.把这个新列表作为值,找不到的那个键作为它的键,放入字典中
3.返回这个列表的引用
这个用来生成默认值的可调用对象存放在名为default_factory的实例属性中。如果在创建defaultdict的时候没有指定default_factory,查询不存在的键会触发KeyError。
方法二(__missing__)如下:
class StrKeyDict0(dict):
def __missing__(self, key):
if isinstance(key, str): # 如果找不到的键本身就是字符串,那就抛出异常
raise KeyError(key)
return self[str(key)] # 如果找不到的键是字符串,那么把它转换成字符串再进行查找
def get(self, key, default=None):
try:
# get方法把查找工作用self[key]的形式委托给__getitem__,这样在宣布查找失败之前
# 还能通过 __missing__ 再给某个键一个机会
return self[key]
except KeyError:
return default
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys()
if __name__ == "__main__":
d = StrKeyDict0([('2', 'two'), ('4', 'four')])
print(d[2])
print(d['2'])
print(d.get('2'))
print(d.get(2))
print(d.get(1, '不存在'))
注意:__missing__方法只会被__getitem__调用(比如在表达式d[k]中)。像 k in dict.keys() 这种操作在Python3中是很快的,而且即便映射类型对象很庞大也没关系,因为dict.keys()返回的值是一个“视图”。视图就像一个集合,而且跟字典类似的是,在视图里查找一个元素的速度很快。
Collections.Counter 一个很有用的映射类型,这个类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列对象技术,或者是当做多重集来用——对重集合就是集合里的元素可以出现不止一次。
ct = collections.Counter(["Hello", "sss", "sss", "sss", "hello"])
print(ct)
5.不可变映射类型
从Python3.3开始,types模块引入了一个封装类名叫MappingProxyType。如果给这个类一个映射,它会返回一个只读的映射视图。虽然是个只读的视图,但是它是动态的,即如果原映射出现了改动,那么我们可以通过这个视图观察到,但是无法通过这个视图对原映射进行修改。
from types import MappingProxyType
d = {1: 'A'}
d_proxy = MappingProxyType(d)
print(d_proxy) # d中的内容可以通过d_proxy看到
# d_proxy[1] = 'X' # 但是通过d_proxy并不能做任何修改
print(d_proxy)
d[1] = 'X'
print(d_proxy) # d_proxy是动态的,即对d所做的任何修改都会反馈到它上面