8.7. 集合 — 唯一元素的无序集合 — Python 文档
8.7. 套 — 独特元素的无序集合
2.3 版中的新功能。
sets 模块提供了用于构造和操作唯一元素的无序集合的类。 常见用途包括成员资格测试、从序列中删除重复项以及计算交集、并集、差和对称差等集合的标准数学运算。
与其他集合一样,集合支持 [X37X]、len(set)
和 for x in set
。 作为一个无序集合,集合不记录元素位置或插入顺序。 因此,集合不支持索引、切片或其他类似序列的行为。
大多数设置应用程序使用 Set 类,该类提供除 __hash__()
之外的所有设置方法。 对于需要散列方法的高级应用程序,ImmutableSet 类添加了一个 __hash__()
方法,但省略了改变集合内容的方法。 Set 和 ImmutableSet 都派生自 BaseSet
,一个用于确定某事物是否为集合的抽象类:isinstance(obj, BaseSet)
。
集合类是使用字典实现的。 因此,集合元素的要求与字典键的要求相同; 也就是说,该元素定义了 __eq__()
和 __hash__()
。 因此,集合不能包含可变元素,例如列表或字典。 但是,它们可以包含不可变的集合,例如 ImmutableSet 的元组或实例。 为了方便实现集合的集合,内部集合自动转换为不可变形式,例如将Set([Set(['dog'])])
转换为Set([ImmutableSet(['dog'])])
。
- class sets.Set([iterable])
- 构造一个新的空 Set 对象。 如果提供了可选的 iterable 参数,则使用从迭代中获得的元素更新集合。 iterable 中的所有元素都应该是不可变的,或者可以使用 部分中描述的协议转换为不可变的,用于自动转换为不可变的 。
- class sets.ImmutableSet([iterable])
构造一个新的空 ImmutableSet 对象。 如果提供了可选的 iterable 参数,则使用从迭代中获得的元素更新集合。 iterable 中的所有元素都应该是不可变的,或者可以使用 部分中描述的协议转换为不可变的,用于自动转换为不可变的 。
因为 ImmutableSet 对象提供了
__hash__()
方法,所以它们可以用作集合元素或字典键。 ImmutableSet 对象没有添加或删除元素的方法,因此在调用构造函数时必须知道所有元素。
8.7.1. 设置对象
Set 和 ImmutableSet 的实例都提供以下操作:
操作 | 等价物 | 结果 |
---|---|---|
len(s)
|
集合 s 中的元素数(基数) | |
x in s
|
测试 x 的成员资格 s | |
x not in s
|
在 s 中测试 x 的非会员资格 | |
s.issubset(t)
|
s <= t
|
测试 s 中的每个元素是否都在 t 中 |
s.issuperset(t)
|
s >= t
|
测试 t 中的每个元素是否都在 s 中 |
s.union(t)
|
t | 包含 s 和 t 元素的新集合 |
s.intersection(t)
|
s & t
|
具有 s 和 t 共有元素的新集合 |
s.difference(t)
|
s - t
|
新集合的元素在 s 但不在 t |
s.symmetric_difference(t)
|
s ^ t
|
具有 s 或 t 中的元素但不是两者的新集合 |
s.copy()
|
带有 s 浅拷贝的新集合 |
请注意,union()
、intersection()
、difference()
和 symmetric_difference()
的非运算符版本将接受任何可迭代对象作为参数。 相比之下,它们的基于运算符的对应物需要设置它们的参数。 这排除了像 Set('abc') & 'cbs'
这样容易出错的结构,而有利于更易读的 Set('abc').intersection('cbs')
。
2.3.1 版本变化: 以前所有参数都需要设置。
此外,Set 和 ImmutableSet 都支持 set 设置比较。 两个集合相等当且仅当每个集合的每个元素都包含在另一个集合中(每个都是另一个的子集)。 一个集合小于另一个集合当且仅当第一个集合是第二个集合的一个真子集(是一个子集,但不相等)。 一个集合大于另一个集合当且仅当第一个集合是第二个集合的真超集(是一个超集,但不相等)。
子集和相等比较不能推广到完整的排序函数。 例如,任意两个不相交的集合不相等,也不是彼此的子集,所以下面的 all 返回 False
: a<b
, a==b
, 或 a>b
。 因此,集合不实现 __cmp__()
方法。
由于集合仅定义部分排序(子集关系),因此 list.sort()
方法的输出对于集合列表是未定义的。
下表列出了在 ImmutableSet 中可用但在 Set 中找不到的操作:
操作 | 结果 |
---|---|
hash(s)
|
返回 s 的哈希值 |
下表列出了在 Set 中可用但在 ImmutableSet 中找不到的操作:
操作 | 等价物 | 结果 |
---|---|---|
s.update(t)
|
= t | 返回集合 s 与从 t 添加的元素 |
s.intersection_update(t)
|
秒 &= 吨 | 返回集 s 只保留也在 t 中找到的元素 |
s.difference_update(t)
|
s -= t | 删除在 t 中找到的元素后返回集合 s |
s.symmetric_difference_update(t)
|
s ^= t | 返回集合 s 与来自 s 或 t 的元素但不是两者 |
s.add(x)
|
添加元素 x 以设置 s | |
s.remove(x)
|
从集合 s 中移除 x; 如果不存在,则引发 KeyError
| |
s.discard(x)
|
从集合 s 中删除 x(如果存在) | |
s.pop()
|
从 s 中移除并返回任意元素; 如果为空,则引发 KeyError
| |
s.clear()
|
从集合 s 中删除所有元素 |
请注意,update()
、intersection_update()
、difference_update()
和 symmetric_difference_update()
的非运算符版本将接受任何可迭代对象作为参数。
2.3.1 版本变化: 以前所有参数都需要设置。
另请注意,该模块还包含一个 union_update()
方法,它是 update()
的别名。 包含该方法是为了向后兼容。 程序员应该更喜欢 update()
方法,因为它被内置的 set() 和 frozenset() 类型支持。
8.7.2. 例子
>>> from sets import Set
>>> engineers = Set(['John', 'Jane', 'Jack', 'Janice'])
>>> programmers = Set(['Jack', 'Sam', 'Susan', 'Janice'])
>>> managers = Set(['Jane', 'Jack', 'Susan', 'Zack'])
>>> employees = engineers | programmers | managers # union
>>> engineering_management = engineers & managers # intersection
>>> fulltime_management = managers - engineers - programmers # difference
>>> engineers.add('Marvin') # add element
>>> print engineers
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
>>> employees.issuperset(engineers) # superset test
False
>>> employees.update(engineers) # update from another set
>>> employees.issuperset(engineers)
True
>>> for group in [engineers, programmers, managers, employees]:
... group.discard('Susan') # unconditionally remove element
... print group
...
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
Set(['Janice', 'Jack', 'Sam'])
Set(['Jane', 'Zack', 'Jack'])
Set(['Jack', 'Sam', 'Jane', 'Marvin', 'Janice', 'John', 'Zack'])
8.7.3. 自动转换为不可变的协议
集合只能包含不可变元素。 为方便起见,可变 Set 对象在作为集合元素添加之前会自动复制到 ImmutableSet。
该机制是始终添加一个 hashable 元素,或者如果它不可散列,则检查该元素以查看它是否具有返回不可变等效项的 __as_immutable__()
方法。
由于 Set 对象有一个 __as_immutable__()
方法返回一个 ImmutableSet 的实例,所以可以构造集合的集合。
__contains__()
和 remove()
方法需要类似的机制,它们需要散列元素以检查集合中的成员资格。 这些方法检查元素的可哈希性,如果不是,则检查 __as_temporarily_immutable__()
方法,该方法返回由类包装的元素,该类为 __hash__()
、__eq__()
和__ne__()
。
备用机制无需构建原始可变对象的单独副本。
Set 对象实现了 __as_temporarily_immutable__()
方法,该方法返回由新类 _TemporarilyImmutableSet
包装的 Set 对象。
这两种增加哈希能力的机制通常对用户是不可见的; 但是,在多线程环境中可能会出现冲突,其中一个线程正在更新一个集合,而另一个线程将其临时包装在 _TemporarilyImmutableSet
中。 换句话说,可变集的集合不是线程安全的。
8.7.4. 与内置的比较放类型
内置的 set 和 frozenset 类型是根据从 sets 模块中吸取的经验教训设计的。 主要区别是:
- Set 和 ImmutableSet 被重命名为 set 和 frozenset。
- 没有等效于
BaseSet
。 而是使用isinstance(x, (set, frozenset))
。 - 对于大多数数据集,内置函数的哈希算法性能明显更好(冲突更少)。
- 内置版本具有更节省空间的泡菜。
- 内置版本没有
union_update()
方法。 相反,使用等效的update()
方法。 - 内置版本没有
_repr(sorted=True)
方法。 相反,使用内置的 repr() 和 sorted() 函数:repr(sorted(s))
。 - 内置版本没有自动转换为不可变的协议。 许多人发现此功能令人困惑,社区中没有人报告说找到了它的真正用途。