用setdefault处理找不到的键
对于如下两段代码,所实现的功能都是一样的。
if key not in my_dict:
my_dict[key] = []
my_dict[key].append(new_value)
my_dict.setdefault(key, []).append(new_value)
相较而言,使用setdefault
来处理字典中不存在的键时,更加优雅。
用defaultdict处理找不到的键
defaultdict
是一个字典类型。对于上面代码中解决的键不存在问题,我们可以用defaultdict
类型解决。
my_dd = collections.defaultdict(list)
my_dd[key].append(new_value)
当表达式my_dd[key]
中键key
不存在时,会进行如下步骤:
(1) 调用list()
来建立一个新列表。
(2) 把这个新列表作为值, key
作为它的键, 放到my_dd
中。
(3) 返回这个列表的引用。
特殊方法_missing_
所有的映射类型(如dict
)在处理不存在的键时,都会调用到__missing__
方法。
下面这个示例是定义了一个StrKeyDict0
类,在查询过程中会将非字符串的键转换成字符串。
# BEGIN STRKEYDICT0
class StrKeyDict0(dict): # 继承了dict
def __missing__(self, key):
if isinstance(key, str): # 如果不存在的键是字符串,抛出异常
raise KeyError(key)
return self[str(key)] # 如果不存在的键不是字符串,将其转换成字符串后再进行查询
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys()
# END STRKEYDICT0
代码中__missing__
方法只会被__getitem__
方法调用,即当d[k]
中k
键不存在时会被调用。而get
或者__contains__
(in 运算符会用调用这个方法) 这些方法遇到不在存在键时则不会被调用。 所以为了能满足程序需求,上述代码中重写了get
和__contains__
方法,使其能够应对键为非字符串的情况。
字典的变种
-
collections.defaultdict
在构造时可以提供一个可调用对象(如上述代码中的
list
),当d[k]
中k
不存在时会被调用并返回某种默认值(如k:[]
)。 -
collections.OrderedDict
这个类型在添加键的时候会保持顺序, 因此键的迭代次序总是一致的。
OrderedDict
的popitem
方法默认删除并返回的是字典里的最后一个元素, 但是如果像my_odict.popitem(last=False)
这样调用它, 那么它删除并返回第一个被添加进去的元素。 -
collections.ChainMap
这个类型可以容纳数个不同的映射对象, 然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。
-
collections.Counter
这个类型会给键准备一个整数计数器。 每次更新一个键的时候都会增加这个计数器。
most_common([n])
会按照次序返回映射里最常见的n
个键和它们的计数。>>> ct = collections.Counter('abracadabra') >>> ct Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}) >>> ct.update('aaaaazzz') >>> ct Counter({'a': 10, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1}) >>> ct.most_common(2) [('a', 10), ('z', 3)]
-
colllections.UserDict
跟
OrderedDict、 ChainMap 和 Counter
这些开箱即用的类型不同,UserDict
是让用户继承写子类的。当我们需要创建自定义映射类型时,通过继承UserDict类比继承dict更方便。
不可变映射类型
标准库中的所有映射类型都是可变的,即可以修改某个映射。在types
模块中引入了一个封装类名叫MappingProxyType
,一个映射对象通过这个类后,会得到一个只读的映射视图(无法通过映射视图修改原映射,除非直接对原映射进行修改)。
>>> from types import MappingProxyType
>>> d = {1:'A'}
>>> d_proxy = MappingProxyType(d)
>>> d_proxy
mappingproxy({1: 'A'})
>>> d_proxy[1]
'A'
>>> d_proxy[2] = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> d[2] = 'B'
>>> d_proxy
mappingproxy({1: 'A', 2: 'B'})
>>> d_proxy[2]
'B'
>>>
集合
集合本质是许多唯一对象的聚集。因此集合可用于序列对象的去重。
集合中元素必须是可散列的。set
类型本身是不可散列的,frozenset
类型本身是可散列的。
给定两个集合a
和 b
, 返回的是它们的合集,
得到的是交集, 而 a - b
得到的是差集。
由于散列表的引入,集合的查找功能非常快。
集合的字面量形如{1,2,3}
,空集必须写成set()
而非{}
(Python会认为{}
是空字典)。
集合操作
语法 | 描述 |
---|---|
a & b | 返回集合a和集合b的交集 |
a | b | 返回集合a和集合b的并集 |
a - b | 返回集合a和集合b的差集 |
a ^ b | 返回集合a和集合b的对称差集 |
a.isdisjoint(b) | 查看集合a和集合b是否不相交(没有共同元素) |
c in a | 元素c是否属于集合a |
a <= b / a >= b | 集合a是否为集合b的子集/父集 |
a < b / a > b | 集合a是否为集合b的真子集/真父集 |
其他方法:a.add(c)、a.clear()、a.copy()、a.discard(c)
(如果 a 里有 c 这个元素的话, 把它移除)、a.pop()、a.remove(c)
等
a、b为集合,c为元素。