一. 集合论
集合的本质是许多唯一对象的聚集,因此,集合可以用于去重:
集合中的元素必须是可散列的,set 类型本身是不可散列的,但是 frozenset 可以。除了保证唯一性,集合还实现了很多基础的中缀运算符。给定两个集合 a 和 b,a | b 返回的是它们的合集,a & b 得到的是交集,而 a - b 得到的是差集。
演示1 needles 的元素在 haystack 里出现的次数,两个变量都是 set 类型
演示2 needles 的元素在 haystack 里出现的次数,这次的代码可以用在任何可迭代对象上
以上的所有例子的运行时间都能在 3 毫秒左右,在含有 10 000 000 个元素的 haystack 里搜索 1000 个值,算下来大概是每个元素 3 微秒。除了速度极快的查找功能(这也得归功于它背后的散列表),内置的 set 和 frozenset 提供了丰富的功能和操作,不但让创建集合的方式丰富多彩,而且对于 set 来讲,我们还可以对集合里已有的元素进行修改。
1.1 集合字面量
除空集之外,集合的字面量——{1}、{1, 2},等等——看起来跟它的数学形式一模一样。如果是空集,那么必须写成 set() 的形式,如果只是写成 {} 的形式,跟以前一样,你创建的其实是个空字典。
像 {1, 2, 3} 这种字面量句法相比于构造方法(set([1, 2, 3]))要更快且更易读。用 dis.dis(反汇编函数)来看看两个方法的字节码的不同:
特殊的字节码 BUILD_SET 几乎完成了所有的工作。而使用set()创建时,3 种不同的操作代替了上面的 BUILD_SET:LOAD_NAME、BUILD_LIST 和 CALL_FUNCTION。
由于 Python 里没有针对 frozenset 的特殊字面量句法,我们只能采用构造方法:
1.2 集合推导
演示2 新建一个 Latin-1 字符集合,该集合里的每个字符的 Unicode 名字里都有“SIGN”这个单词
从 unicodedata 模块里导入 name 函数,用以获取字符的名字。把编码在 32~255 之间的字符的名字里有“SIGN”单词的挑出来,放到一个集合里。
1.3 集合的操作
列出了可变和不可变集合所拥有的方法的概况,其中不少是运算符重载的特殊方法。
中缀运算符需要两侧的被操作对象都是集合类型,但是其他的所有方法则只要求所传入的参数是可迭代对象。例如,想求 4 个聚合类型 a、b、c 和 d 的合集,可以用 a.union(b, c, d),这里 a 必须是个 set,但是 b、c 和 d 则可以是任何类型的可迭代对象。
表 1-1:集合的数学运算:这些方法或者会生成新集合,或者会在条件允许的情况下就地修改集合
数学符号 | Python运算符 | 方法 | 描述 |
S ∩ Z | s & z | s.__add__(z) |
s 和 z 的交集
|
z & s | s.__radd__(z) |
反向 & 操作
|
|
s.intersection(it, ...)
|
把可迭代的 it 和其他所有参数转化为集合,然后求它们与 s 的交集
|
||
s &= z
|
s.__iand__(z) |
把 s 更新为 s 和 z 的交集
|
|
s.intersection_update(it, ...)
|
把可迭代的 it 和其他所有参数转化为集合,然后求得它们与 s 的交集,然后把 s 更新成这个交集
|
||
S ∪ Z
|
s | z
|
s.__or__(z) |
s 和 z 的并集
|
z | s
|
s.__ror__(z) |
| 的反向操作
|
|
s.union(it, ...) |
把可迭代的 it 和其他所有参数转化为集合,然后求它们和 s 的并集
|
||
s |= z
|
s.__ior__(z) |
把 s 更新为 s 和 z 的并集
|
|
s.update(it, ...) |
把可迭代的 it 和其他所有参数转化为集合,然后求它们和 s 的并集,并把 s 更新成这个并集
|
||
S \ Z
|
s - z | s.__sub__(z) |
s 和 z 的差集,或者叫作相对补集
|
z - s | s.__rsub__(z) |
- 的反向操作
|
|
s.difference(it, ...)
|
把可迭代的 it 和其他所有参数转化为集合,然后求它们和 s 的差集
|
||
s -= z | s.__isub__(z) |
把 s 更新为它与 z 的差集
|
|
s.difference_update(it, ...)
|
把可迭代的 it 和其他所有参数转化为集合,求它们和 s 的差集,然后把 s 更新成这个差集
|
||
s.symmetric_difference(it)
|
求 s 和 set(it) 的对称差集
|
||
S △ Z
|
s ^ z
|
s.__xor__(z) |
求 s 和 z 的对称差集
|
z ^ s
|
s.__rxor__(z) |
^ 的反向操作
|
|
s.symmetric_difference_update(it, ...)
|
把可迭代的 it 和其他所有参数转化为集合,然后求它们和 s 的对称差集,最后把 s 更新成该结果
|
||
s ^= z
|
s.__ixor__(z) |
把 s 更新成它与 z 的对称差集
|
表 1-2:集合的比较运算符,返回值是布尔类型
数学符号 | Python运算符 | 方法 | 描述 |
—— | —— | s.isdisjoint(z) |
查看 s 和 z 是否不相交(没有共同元素)
|
e ∈ S
|
e in s | s.__contains__(e) |
元素 e 是否属于 s
|
S ⊆ Z
|
s <= z | s.__le__(z) |
s 是否为 z 的子集
|
s.issubset(it) |
把可迭代的 it 转化为集合,然后查看 s 是否为它的子集
|
||
S ⊂ Z
|
s < z | s.__lt__(z) |
s 是否为 z 的真子集
|
S ⊇ Z
|
s >= z | s.__ge__(z) |
s 是否为 z 的父集
|
s.issuperset(it)
|
把可迭代的 it 转化为集合,然后查看 s 是否为它的父集
|
||
S ⊃ Z
|
s > z | s.__gt__(z) |
s 是否为 z 的真父集
|
表 1-3:集合类型的其他方法
方法 | set | frozenset | 描述 |
s.add(e) | √ | × |
把元素 e 添加到 s 中
|
s.clear()
|
√ | × |
移除掉 s 中的所有元素
|
s.copy() | √ | √ |
对 s 浅复制
|
s.discard(e)
|
√ | × |
如果 s 里有 e 这个元素的话,把它移除
|
s.__iter__()
|
√ | √ |
返回 s 的迭代器
|
s.__len__()
|
√ | √ | len(s) |
s.pop()
|
√ | × |
从 s 中移除一个元素并返回它的值,若 s 为空,则抛出 KeyError 异常
|
s.remove(e)
|
√ | × |
从 s 中移除 e 元素,若 e 元素不存在,则抛出 KeyError 异常
|