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
posted @ 2019-07-22 22:40  听雨危楼  阅读(894)  评论(0编辑  收藏  举报