9、Python3 集合应用
set 翻译为集合,是一个无序的、可变的、不重复的元素序列。
collection 翻译为集合类型,是一个大概念。
可以使用大括号 { } 或者 set() 函数创建集合。
注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
9.1、set 定义
- set() -> new empty set object
- set(iterable) -> new set object
>>> s1 = set()
>>> type(s1)
set
>>> s2 = set(range(5))
>>> print(s2)
{0, 1, 2, 3, 4}
>>> s3 = set(list(range(10)))
>>> print(s3)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> s4 = {}
>>> type(s4
dict
>>> s5 = {1,2,3,4}
>>> type(s5)
set
>>> s6 = {(1,2),3,'a'}
>>> print(s6)
{(1, 2), 'a', 3}
>>> s7 = {[1],(1,),1}
>>> print(s7)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-85-3bcf3ef8dc67> in <module>
----> 1 s7 = {[1],(1,),1}
2 print(s7)
TypeError: unhashable type: 'list'
9.2、set 的元素
- set 的元素要求必须可以 hash。
- 目前学过的不可 hash 的类型有 list、set、dict。
- 元素不可以索引。
- set 可以迭代。
9.3、set 增加
9.3.1、add(elem)
- 增加一个元素到set中
- 如果元素存在,什么都不做
s = set([1,2,3,4])
print(s)
s.add('abc')
print(s)
s.add(1)
print(s)
输出结果:
{1, 2, 3, 4}
{1, 2, 3, 4, 'abc'}
{1, 2, 3, 4, 'abc'}
9.3.2、update(*others)
- 合并其他元素到 set 集合中来
- 参数 others 必须是可迭代对象
- 就地修改
s = set([1,2,3,4])
s.update(('a','b','c'))
print(s)
s.update(['o','p','q'])
print(s)
s.update({"x":50,"y":60,"z":70})
print(s)
输出结果:
{1, 2, 3, 4, 'b', 'c', 'a'}
{1, 2, 3, 4, 'b', 'c', 'o', 'q', 'p', 'a'}
{1, 2, 3, 4, 'b', 'c', 'o', 'z', 'q', 'p', 'a', 'x', 'y'}
9.4、set 删除
9.4.1、remove(elem)
- 从set中移除一个元素
- 元素不存在,抛出KeyError异常。为什么是KeyError?
s = set([1,2,3,4])
print(s)
s.remove(7)
输出结果:
{1, 2, 3, 4}
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-99-7d9c0c061566> in <module>
1 s = set([1,2,3,4])
2 print(s)
----> 3 s.remove(7)
KeyError: 7
9.4.2、discard(elem)
- 从 set 中移除一个元素
- 元素不存在,什么都不做
s = set([1,2,3,4])
print(s)
s.discard(7)
输出结果:
{1, 2, 3, 4}
9.4.3、pop() -> item
- 移除并返回任意的元素。为什么是任意元素?
- 空集返回 KeyError 异常
s = set([1,2,3,4])
print(s.pop())
print(s.pop())
print(s.pop())
print(s.pop())
print(s.pop())
输出结果:
1
2
3
4
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-114-29aa55ccdae7> in <module>
4 print(s.pop())
5 print(s.pop())
----> 6 print(s.pop())
KeyError: 'pop from an empty set'
9.4.4、clear()
- 移除所有元素
s = set([1,2,3,4])
s.clear()
print(s)
输出结果:
set()
9.5、set 修改、查询
修改:
- 要么删除,要么加入新的元素
- 为什么没有修改?
查询:
- 非线性结构,无法索引
遍历:
- 可以迭代所有元素
成员运算符:
- in 和 not in 判断元素是否在 set 中
- 效率呢?
%%timeit lst1 = list(range(100))
a = -1 in lst1
---------------------------------------------------------------------------
1.16 µs ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%timeit lst1 = list(range(1000000))
a = -1 in lst1
---------------------------------------------------------------------------
11.5 ms ± 74.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit set1 = set(range(100))
a = -1 in set1
---------------------------------------------------------------------------
32.7 ns ± 0.272 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%%timeit set1 = set(range(1000000))
a = -1 in set1
---------------------------------------------------------------------------
34.1 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit set1 = set(range(100))
---------------------------------------------------------------------------
2.44 µs ± 28.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit set1 = set(range(1000000))
---------------------------------------------------------------------------
103 ms ± 2.45 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit lst1 = list(range(100))
---------------------------------------------------------------------------
1.11 µs ± 39.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit lst1 = list(range(1000000))
---------------------------------------------------------------------------
41.5 ms ± 227 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
9.6、set 和线性结构
- 线性结构的查询时间复杂度是 O(n),即随着数据规模的增大而增加耗时。
- set、dict 等结构,内部使用 hash 值作为 key,时间复杂度可以做到 O(1),查询时间和数据规模无关。
- set 的元素必须是可 hash 的。
9.7、可 hash
- 数值型 int、float、complex
- 布尔型 True、False
- 字符串 string、bytes
- tuple
- None
- 以上都是不可变类型,成为可哈希类型,hashable
9.8、集合基本概念
全集:
- 所有元素的集合。例如实数集,所有实数组成的集合就是全集。
子集 subset 和超集 superset:
- 一个集合A所有元素都在另一个集合 B 内,A 是 B 的子集,B 是 A 的超集。
真子集和真超集:
- A 是 B 的子集,且 A 不等于 B,A 就是 B 的真子集,B 是 A 的真超集。
并集:
- 多个集合合并的结果。
交集:
- 多个集合的公共部分。
差集:
- 集合中除去和其他集合公共部分。
9.9、集合运算
并集:
- 将两个集合 A 和 B 的所有的元素合并到一起,组成的集合称作集合 A 与集合 B 的并集。
- union(*others),返回和多个集合合并后的新的集合。
- | 运算符重载,等同 union。
- update(*others),和多个集合合并,就地修改。
- |=,等同 update。
a = {1,2,3,4}
b = {5,6,7,8}
print(a|b)
a |= b
print(a)
print(b)
输出结果:
{1, 2, 3, 4, 5, 6, 7, 8}
{1, 2, 3, 4, 5, 6, 7, 8}
{8, 5, 6, 7}
交集:
- 集合 A 和 B,由所有属于 A 且属于 B 的元素组成的集合。
- intersection(*others),返回和多个集合的交集。
- &,等同 intersection。
- intersection_update(*others),获取和多个集合的交集,并就地修改。
- &=,等同 intersection_update。
a = {1,2,3,4,5}
b = {3,5,6,7,8}
print(a & b)
a &= b
print(a)
print(b)
输出结果:
{3, 5}
{3, 5}
{8, 3, 5, 6, 7}
差集:
- 集合 A 和 B,由所有属于 A 且不属于 B 的元素组成的集合。
- difference(*others),返回和多个集合的差集。
- -,等同 difference。
- difference_update(*others),获取和多个集合的差集并就地修改。
- -=,等同 difference_update。
a = {1,2,3,4,5}
b = {3,5,6,7,8}
print(a - b)
a -= b
print(a)
print(b)
输出结果:
{1, 2, 4}
{1, 2, 4}
{8, 3, 5, 6, 7}
对称差集:
- 集合 A 和 B,由所有不属于 A 和 B 的交集元素组成的集合,记作(A-B)∪(B-A)。
- symmetric_differece(other),返回和另一个集合的差集。
- ^,等同 symmetric_differece。
- symmetric_differece_update(other),获取和另一个集合的差集并就地修改。
- ^=,等同 symmetric_differece_update。
a = {1,2,3,4,5}
b = {3,5,6,7,8}
print(a ^ b)
a ^= b
print(a)
print(b)
输出结果:
{1, 2, 4, 6, 7, 8}
{1, 2, 4, 6, 7, 8}
{8, 3, 5, 6, 7}
issubset(other)、<=
:判断当前集合是否是另一个集合的子集。
set1 < set2
:判断 set1 是否是 set2 的真子集。
issuperset(other)、>=
:判断当前集合是否是 other 的超集。
set1 > set2
:判断 set1 是否是 set 的真超集。
isdisjoint(other)
:当前集合和另一个集合没有交集,没有交集,返回 True。
9.10、集合内置方法完整列表
方法 | 描述 |
---|---|
add() | 为集合添加元素 |
clear() | 移除集合中的所有元素 |
copy() | 拷贝一个集合 |
difference() | 返回多个集合的差集 |
difference_update() | 移除集合中的元素,该元素在指定的集合也存在。 |
discard() | 删除集合中指定的元素 |
intersection() | 返回集合的交集 |
intersection_update() | 返回集合的交集。 |
isdisjoint() | 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。 |
issubset() | 判断指定集合是否为该方法参数集合的子集。 |
issuperset() | 判断该方法的参数集合是否为指定集合的子集 |
pop() | 随机移除元素 |
remove() | 移除指定元素 |
symmetric_difference() | 返回两个集合中不重复的元素集合。 |
symmetric_difference_update() | 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中。 |
union() | 返回两个集合的并集 |
update() | 给集合添加元素 |
9.11、练习
- 你的好友 A、B、C,他的好友 C、B、D,求共同好友?
s1 = {'A','B','C'}
s2 = {'C','B','D'}
print(s1 & s2)
输出结果:
{'B', 'C'}
- 有一个API,要求权限同时具备A、B、C才能访问,用户权限是B、C、D,判断用户是否能够访问该API?
API集合A,权限集合P
1、A - P = {}, 说明P包含A
2、A.issubset(P)也行,A是P的子集也行
3、A & P = A也行
- 有一个API,要求权限具备A、B、C任意一项就可访问,用户权限是B、C、D,判断用户是否能够访问该API?
API集合A,权限集合P
1、A & P != {} 就可以
2、A.isdisjoint(P) == False 表示有交集
-
随机产生 2 组各 10 个数字的列表,如下要求:
每个数字取值范围 [10,20]?
统计 20 个数字中,一共有多少个不同的数字?
2 组比较,不重复的数字有几个?分别是什么?
2 组比较,重复的数字有几个?分别是什么?
a = [random.randint(10,20) for i in range(10)]
b = [random.randint(10,20) for i in range(10)]
set1 = set(a)
set2 = set(b)
print(set1)
print(set2)
print(set1 | set2)
print(set1 ^ set2)
print(set1 & set2)
输出结果:
{10, 14, 16, 17, 18, 19}
{11, 12, 13, 14, 17, 18, 19}
{10, 11, 12, 13, 14, 16, 17, 18, 19}
{16, 10, 11, 12, 13}
{17, 18, 19, 14}