标准库里所有的映射类型都是可变的,但有时候你会有这样的需求,比如不能让用户错误地修改某个映射。
从 Python 3.3 开始,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 "", line 1, in
TypeError: 'mappingproxy' object does not support item assignment
# 对d的修改会反馈到d_proxy上面
>>> d[2] = 'B'
>>> d_proxy
mappingproxy({1: 'A', 2: 'B'})
>>> d_proxy[2]
'B'
3.8 集合论
集合的本质是许多唯一对象的聚集。因此,集合可以用于去重:
>>> l = ['spam', 'spam', 'eggs', 'spam']
>>> set(l)
{'eggs', 'spam'}
>>> list(set(l))
['eggs', 'spam']
集合中的元素必须是可散列的,set 类型本身是不可散列的,但是 frozenset 可以。因此可以创建一个包含不同 frozenset 的 set。
除了保证唯一性,集合还实现了很多基础的中缀运算符。给定两个集合 a 和 b,a | b 返回的是它们的合集,a & b 得到的是交集,而 a - b 得到的是差集。合理地利用这些操作,不仅能够让代码的行数变少,还 能减少 Python 程序的运行时间。这样做同时也是为了让代码更易读,从 而更容易判断程序的正确性,因为利用这些运算符可以省去不必要的循环和逻辑操作。
needles 的元素在 haystack 里出现的次数
# 1.要求两个对象都是集合
found = len(needles & haystack)
# 2.两个对象为可迭代对象
found = 0
for n in needles:
if n in haystack:
found += 1
# 3.needles 的元素在 haystack 里出现的次数,这次的代 码可以用在任何可迭代对象上
found = len(set(needles) & set(haystack))
# 另一种写法:
found = len(set(needles).intersection(haystack))
除了速度极快的查找功能(这也得归功于它背后的散列表),内置的 set 和 frozenset 提供了丰富的功能和操作。
3.8.1 集合字面量
除空集之外,集合的字面量——{1}、{1, 2},等等——看起来跟它的 数学形式一模一样。如果是空集,那么必须写成 set() 的形式。
在 Python 3 里面,除了空集,集合的字符串表示形式总是以 {...} 的形 式出现。
>>> s = {1}
>>> type(s)
>>> s
{1}
>>> s.pop()
1
>>> s
set()
像 {1, 2, 3} 这种字面量句法相比于构造方法(set([1, 2, 3]))要更快且更易读。后者的速度要慢一些,因为 Python 必须先从 set 这个 名字来查询构造方法,然后新建一个列表,最后再把这个列表传入到构 造方法里。但是如果是像 {1, 2, 3} 这样的字面量,Python 会利用一 个专门的叫作 BUILD_SET 的字节码来创建集合。
由于 Python 里没有针对 frozenset 的特殊字面量句法,我们只能采用 构造方法。Python 3 里 frozenset 的标准字符串表示形式看起来就像构 造方法调用一样。
3.8.2 集合推导
例子:
>>> from unicodedata import name
>>> {chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i),'')}
{'§', '=', '¢', '#', '¤', '
'°', '+', '÷', '±', '>', '¬', '®', '%'}
3.8.3 集合的操作
pass