集合Set
集合,简称集。由任意个元素构成的集体。高级语言都实现了这个非常重要的数据结构类型。
Python中,它是可变的、无序的、不重复的元素的集合。
hash表
Python中的集合(set)是基于哈希表(Hash Table)实现的,但严格来说,集合并不等同于哈希表,而是使用了哈希表的数据结构来实现其内部机制。
初始化
- set() -> new empty set object
- set(iterable) -> new set object
s1 = set() #输出的就是 set()
s2 = set(range(5))
s3 = set([1, 2, 3])
s4 = set('abcdabcd')
s5 = {} # 这是什么? 输出的就是 {}
s6 = {1, 2, 3}
s7 = {1, (1,)}
s8 = {1, (1,), [1]} # ?报错 unhashable type: 'list' 翻译:不可哈希类型:‘list’
元素性质
-
去重:在集合中,所有元素必须相异
-
无序:因为无序,所以不可索引
-
可哈希:Python集合中的元素必须可以hash,即元素都可以使用内建函数hash
- 目前学过不可hash的类型有:list、set、bytearray
-
可迭代:set中虽然元素不一样,但元素都可以迭代出来
#去重
输入:{*range(5),*[1,2,3]}
输出:{0, 1, 2, 3, 4}
#不可hash
{[1,2]},{{}},{{1}}
TypeError: unhashable type: 'list'
TypeError: unhashable type: 'dict'
TypeError: unhashable type: 'set'
增加
-
add(elem)
增加一个元素到set中
如果元素存在,什么都不做 -
update(*others)
合并其他元素到set集合中来
参数others必须是可迭代对象
就地修改
s = set()
s.add(1) #{1}
s.update((1,2,3), [2,3,4]) #{1,2,3,4}
x = set(1) #{1}
x.add('abc') #{1,'abc'}
x.update('abc') #{1,'a','abc','b','c'}
x.update(['xyz']) #{'a', 'abc', 'b', 'c', 'xyz'}
删除
-
remove(elem)
- 从set中移除一个元素
- 元素不存在,抛出KeyError异常。为什么是KeyError? #唯一不重复
-
discard(elem)
- 从set中移除一个元素
- 元素不存在,什么都不做
-
pop() -> item
- 移除并返回任意的元素。为什么是任意元素?
- 空集返回KeyError异常
-
clear()
- 移除所有元素
s = set(range(10)) #{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s.remove(0) #{1, 2, 3, 4, 5, 6, 7, 8, 9}
#s.remove(11) # KeyError为什么 KeyError: 11 没找到
s.discard(11)
s.pop()
s.clear()#清空
修改
集合类型没有修改。因为元素唯一。如果元素能够加入到集合中,说明它和别的元素不一样。
所谓修改,其实就是把当前元素改成一个完全不同的元素,就是删除加入新元素。
s = {1,2,3}
s.remove{2}
s.add{4}
索引
非线性结构,不可索引。
遍历
只要是容器,都可以遍历元素。但是效率都是O(n)
查询
s = {(1,),2}
1 in s,2 in s #(False, True)
成员运算符in
print(10 in [1, 2, 3])
print(10 in {1, 2, 3})
上面2句代码,分别在列表和集合中搜索元素。如果列表和集合的元素都有100万个,谁的效率高?
答:集合
IPython魔术方法
IPython内置的特殊方法,使用%百分号开头的
- % 开头是line magic
- %% 开头是 cell magic,notebook的cell
%timeit statement
-n 一个循环loop执行语句多少次
-r 循环执行多少次loop,取最好的结果
%%timeit setup_code
* code.....
# 下面写一行,列表每次都要创建,这样写不好
%timeit (-1 in list(range(100)))
# 下面写在一个cell中,写在setup中,列表创建一次
%%timeit l=list(range(1000000))
-1 in l
#这里,%timeit会测量检查-1是否存在于由range(100)生成的集合中的总时间。注意,由于set(range(100))在每次迭代时都会重新创建,这可能会影响性能测量的准确性,因为创建集合本身也是有时间开销的。
%timeit (-1 in set(range(100)))
#当您使用%%timeit时,它会对一个代码块(由多行组成)进行多次执行,并返回每次执行的平均时间。它非常适合测量包含多个语句的代码段的性能。
%%timeit x=set((range(100)))
-1 in x
set和线性结构比较
%%timeit x=list(range(100))
-1 in x
669 ns ± 24.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%timeit x=list(range(1000000))
-1 in x
6.19 ms ± 267 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit x=set(range(100))
-1 in x
22.2 ns ± 1.4 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
%%timeit x=set(range(1000000))
-1 in x
20.6 ns ± 3.74 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
结果说明,集合性能很好。为什么?
- 线性数据结构,搜索元素的时间复杂度是O(n),即随着数据规模增加耗时增大
- set、dict使用hash表实现,内部使用hash值作为key,时间复杂度为O(1),查询时间和数据规模无
关,不会随着数据规模增大而搜索性能下降。
可哈希
- 数值型int、float、complex
- 布尔型True、False
- 字符串string、bytes
- tuple
- None
- 以上都是不可变类型,称为可哈希类型,hashable
set元素必须是可hash的.
集合概念
- 全集
- 所有元素的集合。例如实数集,所有实数组成的集合就是全集
- 子集subset和超集superset
- 一个集合A所有元素都在另一个集合B内,A是B的子集,B是A的超集
- 真子集和真超集
- A是B的子集,且A不等于B,A就是B的真子集,B是A的真超集
- 并集:多个集合合并的结果
- 交集:多个集合的公共部分
- 差集:集合中除去和其他集合公共部分
并集
将两个集合A和B的所有的元素合并到一起,组成的集合称作集合A与集合B的并集
- union(*others) 返回和多个集合合并后的新的集合
- | 运算符重载,等同union
- update(*others) 和多个集合合并,就地修改
- |= 等同update
| 运算符重载,等同union
a = set(range(5))
b = set(range(5,10))
输入:a | b
输出:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|= 等同update
a = set(range(5))
b = set(range(5,10))
输入:a |= b
a,b
输出:({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {5, 6, 7, 8, 9})
交集
集合A和B,由所有属于A且属于B的元素组成的集合
- intersection(*others) 返回和多个集合的交集
- & 等同intersection
- intersection_update(*others) 获取和多个集合的交集,并就地修改
- &= 等同intersection_update
& 等同intersection
a = set(range(5))
b = set(range(3,7))
输入:a & b,a,b
输出:({3, 4}, {0, 1, 2, 3, 4}, {3, 4, 5, 6})
&= 等同intersection_update
a = set(range(5))
b = set(range(3,7))
输入:a &= b
a
输出:{3, 4}
差集
集合A和B,由所有属于A且不属于B的元素组成的集合
- difference(*others) 返回和多个集合的差集
- ‘-’ 等同difference
- difference_update(*others) 获取和多个集合的差集并就地修改
- -= 等同difference_update
- 等同difference
a = set(range(5)) #{0,1,2,3,4}
b = set(range(3,7)) #{3,4,5,6}
输入:a - b
输出:{0, 1, 2}
-= 等同difference_update
a = set(range(5))
b = set(range(3,7))
输入:a - b
a
输出:{0, 1, 2}
#difference(*others) 返回和多个集合的差集
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}
# 计算 set1 和 set2 的差集
result = set1.difference(set2)
print(result) # 输出: {1, 2, 3}
对称差集
集合A和B,由所有不属于A和B的交集元素组成的集合,记作(A-B)∪(B-A)
- symmetric_differece(*other) 返回和另一个集合的对称差集
- ^ 等同symmetric_differece
- symmetric_differece_update(*other) 获取和另一个集合的对称差集并就地修改
- ^= 等同symmetric_differece_update
#^ 等同symmetric_differece
a = set(range(5)) #{0,1,2,3,4}
b = set(range(3,7)) #{3,4,5,6}
输入:a ^ b
输出:{0, 1, 2, 5, 6}
#^= 等同symmetric_differece_update
a = set(range(5))
b = set(range(3,7))
输入:a ^= b
a
输出:{0, 1, 2, 5, 6}
其它集合运算
- issubset(other)、<= 判断当前集合是否是另一个集合的子集
- set1 < set2 判断set1是否是set2的真子集
- issuperset(other)、>= 判断当前集合是否是other的超集
- set1 > set2 判断set1是否是set2的真超集
- isdisjoint(*other) 当前集合和另一个集合没有交集,没有交集,返回True
#issubset(other)、<= 判断当前集合是否是另一个集合的子集
a = set(range(5))
b = set(range(3,7))
输入:a.issubset(b)
输出:False
a = set(range(5)) #{0,1,2,3,4}
b = set(range(7)) #{0,1,2,3,4,5,6}
输入:a.issubset(b)
输出:True
#set1 < set2 判断set1是否是set2的真子集
a = set(range(5))
b = set(range(7))
输入:a < b
输出:True
#issuperset(*other)、>= 判断当前集合是否是other的超集
a = {1, 2, 3, 4}
b = {1, 2}
c = {3, 4}
print(a.issuperset(b)) # 输出: True
print(a.issuperset(c)) # 输出: True
print(a.issuperset(b, c)) # 抛出 TypeError,因为 issuperset 不接受多个集合作为位置参数(除非它们被打包成一个可迭代对象)
print(a.issuperset([1, 2, 5])) # 输出: False,因为 5 不在 a 中
#isdisjoint(*other) 当前集合和另一个集合没有交集,没有交集,返回True
a = set(range(5))
b = set(range(5,7))
输入:a.isdisjoint(b)
输出:True