python 集合

“集”这个概念在 Python 中算是比较年轻的,同时它的使用率也比较低。set 和它的不可变的姊妹类型 frozenset 直到 Python 2.3 才首次以模块的形式出现,然后在 Python 2.6 中它们升级成为内置类型。

集合的本质是许多唯一对象的聚集。因此,集合可以用于去重:

 l = ['spam', 'spam', 'eggs', 'spam']
 set(l)
 # {'eggs', 'spam'}
 list(set(l))
 # ['eggs', 'spam']
 

集合中的元素必须是可散列的,set 类型本身是不可散列的,但是 frozenset 可以。因此可以创建一个包含不同 frozensetset
除了保证唯一性,集合还实现了很多基础的中缀运算符 。给定两个集合 aba | b 返回的是它们的合集,a & b 得到的是交集,而 a - b 得到的是差集。合理地利用这些操作,不仅能够让代码的行数变少,还能减少 Python 程序的运行时间。这样做同时也是为了让代码更易读,从而更容易判断程序的正确性,因为利用这些运算符可以省去不必要的循环和逻辑操作。

例如,我们有一个电子邮件地址的集合(haystack),还要维护一个较小的电子邮件地址集合(needles),然后求出 needles 中有多少地址同时也出现在了 heystack 里。借助集合操作,我们只需要一行代码就可以了。
示例1:

found = len(needles & haystack)

如果不使用交集操作的话,代码可能就变成了下面示例:
示例2:

found = 0
for n in needles:
    if n in haystack:
        found += 1
        

上面示例比下面示例的速度要快一些;另一方面,下面示例可以用在任何可迭代对象needleshaystack 上,而上面示例则要求两个对象都是集合。话再说回来,就算手头没有集合,我们也可以随时建立集合
示例3:

found = len(set(needles) & set(haystack))
# 或者
found = len(set(needles).intersection(haystack))

示例 3 里的这种写法会牵扯到把对象转化为集合的成本,不过如果 needles 或者是haystack 中任意一个对象已经是集合,那么示例 3 的方案可能就比示例 2 里的要更高效。

以上的所有例子的运行时间都能在 3 毫秒左右,在含有 10 000 000 个元素的 haystack 里搜索 1000 个值,算下来大概是每个元素 3 微秒。

除了速度极快的查找功能(这也得归功于它背后的散列表),内置的 setfrozenset 提供了丰富的功能和操作,不但让创建集合的方式丰富多彩,而且对于 set 来讲,我们还可以对集合里已有的元素进行修改。在讨论这些操作之前,先来看一下相关的句法。

集合字面量

除空集之外,集合的字面量——{1}、{1, 2},等等——看起来跟它的数学形式一模一样。如果是空集,那么必须写成 set() 的形式。

注意:
句法的陷阱: 不要忘了,如果要创建一个空集,你必须用不带任何参数的构造方法 set()。如果只是写成 {} 的形式,跟以前一样,你创建的其实是个空字典

python 集合_第1张图片
{1, 2, 3} 这种字面量句法相比于构造方法(set([1, 2, 3]))要更快且更易读。后者的速度要慢一些,因为 Python 必须先从 set 这个名字来查询构造方法,然后新建一个列表,最后再把这个列表传入到构造方法里。但是如果是像 {1, 2, 3} 这样的字面量,Python 会利用一个专门的叫作 BUILD_SET 的字节码来创建集合。

dis.dis反汇编函数)来看看两个方法的字节码的不同
python 集合_第2张图片

  • dis('{1}') 检查 {1} 字面量背后的字节码
  • 特殊的字节码 BUILD_SET 几乎完成了所有的工作
  • dis('set([1])') 检查 set([1]) 的字节码
  • 3 种不同的操作代替了上面的 BUILD_SETLOAD_NAMEBUILD_LISTCALL_FUNCTION

由于 Python 里没有针对 frozenset 的特殊字面量句法,我们只能采用构造方法。Python 3frozenset 的标准字符串表示形式看起来就像构造方法调用一样。来看这段控制台对话:
python 集合_第3张图片

集合推导

  • 从 unicodedata 模块里导入 name 函数,用以获取字符的名字
  • 把编码在 32~255 之间的字符的名字里有“SIGN”单词的挑出来,放到一个集合里。

python 集合_第4张图片

集合的操作

下图列出了可变不可变集合所拥有的方法的概况,其中不少是运算符重载的特殊方法。

collections.abc 中,MutableSet 和它的超类的 UML 类图(箭头从子类指向超类,抽象类和抽象方法的名称以斜体显示,其中省略了反向运算符方法)
python 集合_第5张图片
下表则包含了数学里集合的各种操作在 Python 中所对应的运算符和方法。其中有些运算符和方法会对集合做就地修改(像 &=difference_update,等等),这类操作在纯粹的数学世界里是没有意义的,另外 frozenset 也不会实现这些操作。

集合的数学运算:这些方法或者会生成新集合,或者会在条件允许的情况下就地修改集合
python 集合_第6张图片
表中的中缀运算符需要两侧被操作对象都是集合类型,但是其他的所有方法则只要求所传入的参数是可迭代对象。例如,想求 4 个聚合类型 a、b、cd 的合集,可以用 a.union(b, c, d),这里 a 必须是个 set,但是 b、cd 则可以是任何类型的可迭代对象。

下表里列出了返回值是 TrueFalse 的方法和运算符。

集合的比较运算符,返回值是布尔类型
python 集合_第7张图片
除了跟数学上的集合计算有关的方法和运算符,集合类型还有一些为了实用性而添加的方法。

集合类型的其他方法
python 集合_第8张图片

你可能感兴趣的:(python)