1-Python - set
About
Python 2.3版本的时候,引入了一种新的数据类型——集合(set)。
集合是由序列(也可以是其他的可迭代对象)构建的,是无序的可变的数据类型。
Python中,集合用一对大括号“{}”表示,集合内的各元素用逗号分隔。
s = set()
print(s) # set([])
s1 = {1, 2, 3}
print(s1) # set([1, 2, 3])
注意集合字典的区别,字典虽然也是一对大括号,但是其内是键值对形式的,而集合是用逗号隔开元素的,大家创建集合时需注意。我们也可通过set()函数将列表、元组等其他的可迭代的对象转换为集合。
s = set([1, 2, 1, 3, 4])
print(s) # 3. {1, 2, 3, 4}
可以看出,转换的时候,集合会自动将元素去重,这也是集合的特性:集合内的每个元素都是唯一的,不可重复!
除此之外,集合的元素只能是不可变类型的数据类型,也就是可哈希类型,而比如列表,字典,和集合本身则不可作为集合的元素。
集合的基本操作
首先,我们来说,集合不支持什么操作:
集合不支持:索引、切片,复制,拼接
s = set(range(10))
print(s) # set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# print(s[0]) # TypeError: 'set' object does not support indexing
# print(s[0:3]) # TypeError: 'set' object has no attribute '__getitem__'
s1 = {'a', 'b'}
# s2 = s + s1 # TypeError: unsupported operand type(s) for +: 'set' and 'set'
s2 = s1 * 3 # TypeError: unsupported operand type(s) for *: 'set' and 'int'
可以看到,集合确实不支持这些!
成员资格测试
但是支持什么呀,没错,成员资格测试还是可以的。
s = set(range(10))
print(1 in s) # True
print(1 not in s) # False
for循环取值
s = set(range(10))
for item in s:
print(item)
'''
0
1
2
3
4
5
6
7
8
9
'''
留个思考题,集合能while循环打印其中的值嘛?为啥能for循环取值?
虽然基本操作不多,但是常用方法不少。我们一起来看看。
集合的常用方法
set.add(obj)
为集合添加元素:
s = set()
s.add(1)
s.add(2)
s.add(1)
print(s) # set([1, 2])
由于去重的特性,集合将重复添加的元素过滤掉了。
set1.update(set2)
s1 = {1, 2}
s2 = (1, 2, 3, 4)
s1.update(s2)
print(s1) # set([1, 2, 3, 4])
通过set.update()
将另一个集合(s2)更新到当前集合(s1)中。同样的,会去重。
删除之:set.pop(obj)
s = {1, 2, 3, 4}
print(s.pop()) # 1
print(set().pop()) # KeyError: 'pop from an empty set'
随机删除集合元素并将该元素返回,如果集合为空则抛出KeyError
。
set.remove() & set.discard()
set.remove() & set.discard()
用来删除集合中的指定元素:
s = {1, 2, 3, 4}
s.remove(1)
print(s) # {2, 3, 4}
s.remove('a') # KeyError: 'a'
s1 = {1, 2, 3, 4}
s1.discard(1)
print(s1) # {2, 3, 4}
print(s1.discard('a')) # None
print(s1) # {2, 3, 4}
注意,set.remove()和set.discard()都是用来删除指定的元素,但区别是set.remove()如果指定的元素不存在则会报错,而set.discard()不会报错,而是返回None。
del 删除集合
由于集合是无序的,所以,del只能删除整个集合:
s = {1, 2, 3, 4}
# del s[0] # TypeError: 'set' object doesn't support item deletion
del s
print(s) # NameError: name 's' is not defined
第一个报错是不能删除集合中的元素。第二个是之前已经将变量s
删除,最后引用的时候,发现没有报的错。
set.clear()
set.clear()
清空集合
s = {1, 2, 3, 4}
s.clear()
print(s) # set()
集合的元素嵌套
前文中说集合中的元素只能是不可变类型的数据,所以,集合中的元素就只能是数字、字符串和元组。
s1 = {1, 2.3, 'abc', (1, 2, [3, 4])} # TypeError: unhashable type: 'list'
由第二行的报错来看,元组要想称为集合的元素或者字典的key时,其内的元素必须全部为不可变类型才行。
集合的运算
现在提个需求,现有个培训学校欧德博爱开设了Python和Linux两门课程,来学习的同学都有如下情况:
- 有的同学学习Linux
- 有的学习Python
- 还有的既学了Linux又学了Python
那现在需求来了,我们要对这些同学的情况做统计,比如找出两门课都报了的同学?
那采用什么数据类型呢?这里先用列表举例。
learn_python = ['小a', '小b', '小c', '小麻雀', '葫芦娃']
learn_linux = ['小c', '小d', '小e', '小东北', '小麻雀']
learn_p_l = []
for p in learn_python:
if p in learn_linux:
learn_p_l.append(p)
print(learn_p_l) # ['小c', '小麻雀']
那要找出只学习了linux的同学呢?
learn_python = ['小a', '小b', '小c', '小麻雀', '葫芦娃']
learn_linux = ['小c', '小d', '小e', '小东北', '小麻雀']
learn_l = []
for l in learn_linux:
if l not in learn_python:
learn_l.append(l)
print(learn_l) # ['小d', '小e', '小东北']
这么做是不是特别麻烦?在Python中,懒惰既是美德!所以Python给我们提供了一种简便的方法,使用集合来做这些事情。
交集
首先来记住三个方法一个运算符:
- set1.intersection(set2, set3...),以新集合的形式返回两个或多个集合的交集。
- set1.intersection_update(set2),两个集合求交集(大家都有的),然后将结果覆盖(相当于先对set1做clear操作)到当前(原地操作,并没有产生新的集合)集合中(set1)。
&
运算符,intersection()方法对应的运算符,其实就是简写形式。- set1.isdisjoint(set2),该方法用来判断两个集合是否有交集,如果有交集,返回False,没有交集返回True。
示例:找出即学习了Python又学习了Linux的同学。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
new_set1 = learn_python.intersection(learn_linux)
print(new_set1) # {'小麻雀', '小c'}
new_set2 = learn_python & learn_linux
print(new_set2) # {'小麻雀', '小c'}
print(learn_python) # {'葫芦娃', '小a', '小麻雀', '小b', '小c'}
intersection()
方法将结果以新的集合形式返回,并没有改变原集合。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
learn_python.intersection_update(learn_linux)
print(learn_python) # {'小c', '小麻雀'}
可以发现,intersection_update()
方法求出结果后,并没有赋值给新的集合,而是将结果(set1 & set2)覆盖到(可以理解为先清空)set1集合中。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
learn_java = {'Neeo', 'Anthony'}
print(learn_python.isdisjoint(learn_linux)) # False
print(learn_python.isdisjoint(learn_java)) # True
可以发现,Python和Linux两门学科是有同学同时学习的,所以返回了False。而没有同学同时学了Python和学Java,所以返回了True。
并集
首先来记住求并集的方法和运算符:
- set1.union(set2, set3...),该方法以新集合的形式返回两个或多个集合的并集,既包含了两个集合的所有元素,并且重复的元素只会出现一次(去重)。
|
,求并集的运算符为管道符|
。
示例:找出所有来欧德博爱学习课程的人。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
new_set1 = learn_python.union(learn_linux)
print(new_set1) # {'小东北', '小c', '小e', '葫芦娃', '小d', '小a', '小麻雀', '小b'}
new_set2 = learn_python | learn_linux
print(new_set2) # {'小东北', '小e', '小d', '小c', '小a', '小b', '葫芦娃', '小麻雀'}
差集
首先,来记住求差集的方法和运算符:
- set1.difference(set2, set3...),返回移除两个或多个集合中重复元素后的新集合,也就是只保留我自己(独有的)。
-
,求差集的运算符为-
。- set1.difference_update(set2, set3...),该方法直接在原集合(set1)中移除重复元素,是原地操作,并没有返回值。
找出只学习了Python(或者Linux)课程的人。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
new_set1 = learn_python.difference(learn_linux)
print(new_set1) # {'小b', '葫芦娃', '小a'}
new_set2 = learn_python - learn_linux
print(new_set2) # {'小b', '葫芦娃', '小a'}
print(learn_python) # {'小b', '葫芦娃', '小麻雀', '小c', '小a'}
可以看到,新的集合中,只保留了仅学Python学科的学生,并没有改变原有集合。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
learn_python.difference_update(learn_linux)
print(learn_python) # {'葫芦娃', '小a', '小b'}
当前集合(learn_python)移除与集合(learn_linux)重复的元素后,将结果覆盖到当前集合中(learn_python)。
对称差集
首先,来记住求差集的方法和运算符:
- set1.symmetric_difference(set2),返回两个或多个集合中不重复元素后的新集合。也就是你独有的加上我独有的,合一起后的结果。
^
,求对称差集的运算符为^
。- set1.symmetric_difference_update(set2),该方法返回了两个或多个集合中不重复元素,并将结果覆盖到当前集合中(set1),是原地操作,没有返回值。
示例:找出没有同时学习Python和Linux的学生。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
new_set1 = learn_python.symmetric_difference(learn_linux)
print(new_set1) # {'葫芦娃', '小a', '小e', '小d', '小东北', '小b'}
new_set2 = learn_python ^ learn_linux
print(new_set2) # {'小d', '葫芦娃', '小a', '小b', '小东北', '小e'}
print(learn_python) # {'葫芦娃', '小麻雀', '小b', '小c', '小a'}
有前两个打印结果可以看到,移除两个集合的交集部分就是对称差集的结果。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫芦娃'}
learn_linux = {'小c', '小d', '小e', '小东北', '小麻雀'}
learn_python.symmetric_difference_update(learn_linux)
print(learn_python) # {'小e', '葫芦娃', '小b', '小东北', '小d', '小a'}
通过打印可以看到,在求完对称差集后,将结果覆盖到当前的集合中(learn_python)。
子集 & 超集
首先,来记住两个方法:
- set1.issubset(set2),该方法用来判断一个集合是否为另一个集合的子集。
- set1.issuperset(set2),该方法用来判断一个集合是否为另一个集合的超集。
s1 = {1, 2}
s2 = {1, 2, 3, 4}
print(s1.issubset(s2)) # True
print(s1.issuperset(s2)) # False
上例,集合s1是另一个集合s2的子集,或者说集合s2是集合s1的超集。
还有一种情况需要注意:
s1 = {1, 2}
s2 = {1, 2}
print(s1.issubset(s2)) # True
print(s1.issuperset(s2)) # True
上例中,当两个集合元素一致时,两个集合的关系可以称为互为子集。
集合中的关系运算
在集合中,我们可以使用关系运算符来判断两个集合的包含关系。
s1 = {1, 2}
s2 = {1, 2, 3, 4}
print(s1 < s2) # True
print(s1 <= s2) # True
print(s1 > s2) # False
print(s1 >= s2) # False
print(s1 == s2) # False
print(s1 != s2) # True
在集合中,关系运算符符包括:
- 小于(<),一个集合是否小于另一个集合。
- 小于等于(<=),一个集合是否小于等于另一个集合。
- 大于(>),一个集合是否大于另一个集合。
- 大于等于(>=),一个集合是否大于等于另一个集合。
- 等于(==),一个集合是否等于另一个集合。
- 不等于(!=),一个集合是否不等于另一个集合。
再次强调,在集合中,关系运算符只是表示两个集合的包含关系,而不是表示两个集合的大小关系。
方法 | 描述 | 重要程度 |
---|---|---|
set.add(obj) | 添加元素 | ***** |
set1.issuperset(set2) | 判断当前集合是否为另一个集合的超集 | *** |
set1.issubset(set2) | 判断当前集合是否为另一个集合的子集 | *** |
set1.sysmmetric_difference_update(set2) | 求对称差集并将结果更新到当前集合内 | *** |
set1.sysmmetric_difference(set2) | 对称差集:^ | ***** |
set1.update(set2) | 另一个集合更新当前集合 | ***** |
set.copy() | 浅拷贝 | **** |
set.discard(obj) | 删除指定元素 | ***** |
set.pop() | 随机删除集合内元素并将此元素返回 | ***** |
set.clear() | 清空集合 | *** |
set.remove(obj) | 删除指定元素 | **** |
set1.difference_update(set2) | 求差集并将结果更新到当前集合内 | **** |
set1.difference(set2) | 差集: - | ***** |
set1.union(set2) | 并集: | | ***** |
set1. isdisjoint(set2) | 两个集合没有交集返回True,否则返回False | *** |
set1.instersection_update(set2) | 求交集并将结果更新到当前集合内 | **** |
set1.instersection(set2) | 交集: & | ***** |
除此之外,还有以下内置函数可以应用于集合。
内置函数 | 描述 | 重要程度 |
---|---|---|
len(set) | 返回集合的元素个数 | ***** |
max(set) | 返回集合内的最大元素 | **** |
min(set) | 返回集合内的最小元素 | **** |
sum(set) | 返回集合元素之和 | *** |
sorted(set) | 排序集合内的元素 | ***** |
同样的,这些内置函数在应用于集合时,要注意集合内的元素类型,比如求最大或最小时,其中的元素必须是同类型,不然怎么比较呀!
总结
让我们来总结下:
- 按照可变与不可变来说:
- 可变类型:列表、字典、集合。
- 不可变类型:数字、字符串、元组。
- 按照存放值的数量来说:
- 一个值:数字,字符串。
- 多个值:列表、元组、字典、集合。
注意,一串字符串在客观上也可以理解为一个元素,比如在列表中,字符串再长,也只是列表的一个元素。数字也一样。
- 按照取值方式:
- 直接取值:数字。
- 序列类型:字符串、元组、列表。
- 映射类型:字典。
至此,Python内置的数据类型,基本学习完毕。
欢迎斧正,that's all