6. Set(集合)
桃老师不仅是1年级1班的班主任,还同时担任班内的英语老师。在班里第一次英语单词听写后,桃老师想让所有学生把他们听写出错的单词抄一份交上来,老师想根据这份名单看看有哪些单词错误率过高。在这次,桃老师准备用 set(集合) 来储存信息。为什么会选择集合呢?我们先来认识一下这位老哥:
6.1 标志
set(集合)乍看像字典,仔细看又像元组或者列表,但是仔细研究一下发现它又有着与上述三者都不相像的规律。我们先看看这个三不像长什么样,哪的什么部分都像谁,又都不像谁:
a = {'think', 'apple', 'thing'}
乍一看,它有着和字典一样的 {} ,但是里面的元素却不是键值对而是单独的元素;从内部的单一元素看又像列表和元组。这时候我们试着多写几个看看会怎样:
>>> a={'think','apple','thing','think','hito','apple'}
>>> a
{'hito', 'thing', 'apple', 'think'}
这样看,它的一些突出特点就暴露了:集合内不允许添加重复元素,否则自动去重。
元素是无序的,打印出的元素顺序与键入的顺序往往不一致。
这时候我们好像明白桃老师的用意了,选择集合的一个原因是自动去重,这样可以避免对一个单词的重复讲解;还一个原因在于无序性保证的课堂听课质量,小朋友们必须一致保持听讲的认真,因为他们不知道自己错的词是在哪节课出现。因为桃老师太清楚这个道理了:你这次这个单词没错,不意味着你已经掌握了。
6.2 创建
我们怎么创建一个集合呢,一共有两种方式:
第一种方式: 集合名 = {}
>>> a = {'banana','house','pencil'}
>>> print(a)
{'banana','house','pencil'}
第二种方式: set()
>>> set('thinkaboutit')
{'i', 'a', 'n', 't', 'h', 'o', 'u', 'b', 'k'}
观察第二种创建方式我们发现还是存在诸多限制的,例如:
1. 一次只能传入一个字符串:
>>> set('think','about')
TypeError: set expected at most 1 arguments, got 2
翻译:俺最多只要一个,你却给了俺俩。
2. 每个字符会当作一个元素处理,并自动去重:
>>> set('aabooountthat')
{'a', 'n', 't', 'h', 'o', 'u', 'b'}
一共有七个非重复的字符被无序显示了出来。
3. 不允许传入数字:
>>> set(1433.1)
TypeError: 'float' object is not iterable
>>> set(123)
TypeError: 'int' object is not iterable
翻译:俺没法迭代整数型/浮点型啊。。。 我们会发现无论是整数型还是浮点型的数字都不允许被传入。
6.3 集合的基本操作
这个集合在运算方面,可以当作我们初中数学学过的集合一样,进行交集、并集等处理。 比如这是老师收到的 '王有才' 同学、 '李康怡' 同学 和 '郑仕' 同学第一次听写的出错内容:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> zhengshi = {'pencil','ruler'}
>>> wangyoucai - zhengshi
{'sarah', 'home'}
桃老师将上述内容创建成了一个名为 'shengci' 的集合:
shengci = {'banana','pencil','house','home','sarah','ruler'}
为了谨慎,桃老师又把这个集合备份了一下:
(备份)
拷贝的方式为 集合名.copy() ,桃老师试图将其备份到一个名为 Beifen_cuoci01 的地方:
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> Beifen_cuoci1 = shengci.copy()
>>> Beifen_cuoci1
备份成功。
A. 求交集:
第一种方式: 集合名 & 集合名
老师想看一下三位同学都出错的内容,用 集合名 & 集合名 的方式求了下交集:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> zhengshi = {'pencil','ruler'}
>>> likangyi & wangyoucai & zhengshi
{'pencil'}
第二种方式: 集合名.intersection(一个或多个集合名)
关于这个用法有两个要点要强调:
1. 直接输出结果,不会改动原集合内容:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> wangyoucai.intersection(zhouyuan)
{'purple', 'white'}
#我们查看一下原集合内容,发现没有变化:
>>> wangyoucai
{'purple', 'white', 'red'}
因为集合的无序性,所以输出时 'wangyoucai' 内部的顺序已经发生了变化,但是内容是没有变的。
2. 也可以直接赋值到别的变量中,仍然不会改变集合内容:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> a = wangyoucai.intersection(zhouyuan)
>>> a
{'purple', 'white'}
#我们查看一下原集合内容,发现没有变化:
>>> wangyoucai
{'purple', 'white', 'red'}
我们再看一下与第二种方式很像的,第三种方式:
第三种方式: 集合名.intersection_update(一个或多个集合名)
与第二种方式对比来看,还是有两个要点:
1. 并不会直接输出结果,而是将结果直接在原集合上改动:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> wangyoucai.intersection_update(zhouyuan)
>>> wangyoucai
{'white', 'purple'}
2. 不可以进行进行直接赋值:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> a = wangyoucai.intersection_update(zhouyuan)
>>> print(a)
None
结果为None,发现不可以进行直接赋值。
6.3.A 中三种方式总结:
(如果不清楚可以点开大图)
B. 求并集:
第一种方式: 集合名|集合名
再用 集合名 | 集合名 的方式求了下并集,看看三位同学都错了那些词:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> zhengshi = {'pencil','ruler'}
>>> likangyi | wangyoucai | zhengshi
{'pencil', 'banana', 'ruler', 'sarah', 'house', 'home'}
第二种方式: 集合名.union(一个或多个集合名)
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> zhengshi = {'pencil','ruler'}
>>> likangyi.union(wangyoucai, zhengshi)
{'pencil', 'banana', 'ruler', 'sarah', 'house', 'home'}
6.3.B 中两种方式总结:
(如果不清楚可以点开大图)
C. 求集合a中包含而集合b中不包含的元素:
第一种方式: 集合名 - 集合名
这是一种比较特殊的用法,所以只好举(chě)一个比较特殊的例子…
有一天 '王有才' 小朋友和 '周媛' 小朋友各带了三块颜色不一样的糖,但是 '王有才' 小朋友是很特(qian)殊(da)的孩子。他不喜欢别人和他有一样的东西,吃的也一样。 '王有才' 小朋友看了下他们手中糖的颜色:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
'王有才' 不想和她吃一样颜色的糖,他想扔掉所有 '周媛' 小朋友有的糖:
>>> wangyoucai - zhouyuan
>>> wangyoucai
{'red'}
王有才' 本来可以体会连吃三块糖的酸爽,现在只剩一块糖可以吃了,真是个小机灵鬼。
第二种方式: 集合名.difference(集合名)
关于这个用法有两个要点要强调:
1. 直接输出结果,不会改动原集合内容:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> wangyoucai.difference(zhouyuan)
{'red'}
>>> wangyoucai
{'purple', 'white', 'red'}
因为集合的无序性,所以输出时 '王有才' 的顺序已经发生了变化,但是内容是没有变的。
2. 也可以直接赋值到别的变量中:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> a = wangyoucai.difference(zhouyuan)
>>> a
{'red'}
我们再看一下与第二种方式很像的,第三种方式:
第三种方式: 集合名.difference_update(集合名)
与第二种方式对比来看,还是有两个要点:
1. 并不会直接输出结果,而是将结果直接在原集合上改动:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> wangyoucai.difference_update(zhouyuan)
>>> wangyoucai
{'red'}
2. 不可以进行进行直接赋值:
>>> wangyoucai = {'red', 'purple', 'white'}
>>> zhouyuan = {'purple', 'green', 'white'}
>>> a = wangyoucai.difference_update(zhouyuan)
>>> print(a)
None
结果为None,发现不可以进行直接赋值。
6.3.C 中三种方式总结:
(如果不清楚可以点开大图)
D. 求不同时包含于a和b中元素:
第一种方式: 集合名 ^ 集合名
这其实就相当于一个用并集减去交集的过程,结果就是二者不共有的那些东西。
第二种方式: 集合名.symmetric_difference_update(集合名)
嗯…一个好长的方法。这个方法有两个很重要的要点要注意:
1. 最终结果将更新到第一个集合中去:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> likangyi.symmetric_difference_update(wangyoucai)
>>> likangyi
{'banana','house','home','sarah'}
2. 这个方法不可以直接赋给另外一个变量:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> a = likangyi.symmetric_difference_update(wangyoucai)
>>> print(a)
None
我们发现即使将整个语句赋值给了a,可是打印a结果还是为None,所以明白这个语句只能将结果更新到前面的集合中去,而不能直接赋值。
第三种方式: 集合名.symmetric_difference(集合名)
这种方式与第二种很像,主要区别在于:
1. 结果直接输出:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> likangyi.symmetric_difference(wangyoucai)
{'banana','house','home','sarah'}
与第二种方法不同,这种方法输入后不用再输入一行打印的命令,结果将会直接输出。
2. 所有原始集合的内容都不会变动:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> likangyi.symmetric_difference(wangyoucai)
{'banana','house','home','sarah'}
>>> likangyi
{'banana','pencil','house'}
>>> wangyoucai
{'pencil','home','sarah'}
3. 结果可以直接赋给别的变量:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> a = likangyi.symmetric_difference(wangyoucai)
>>> a
{'banana','house','home','sarah'}
6.3.C 中三种方式总结:
(如果不清楚可以点开大图)
6.4 添加元素
桃老师又用 集合名.add(元素) 的方式将三位同学的错词添加到了 shengci 这个集合中:
>>> shengci = set()
>>> shengci.update(likangyi, wangyoucai, zhengshi)
>>> shengci
{'banana','pencil','house','home','sarah','ruler'}
可见其他多个集合也可以通过这个方式传入到一个新的合集中。
6.5 移除元素
第一种方法: 集合名.remove()
后来经过反复听写,班里已经没人再会错 'home' 这个词了,老师要用 集合名.remove() 将这个词从生词表中移除:
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> shengci.remove('home')
>>> shengci
{'banana','pencil','house','sarah','ruler'}
要注意,当删除一个原本不存在的元素时会报错:
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> shengci.remove('what')
KeyError: 'what'
注意:“元素不存在即报错”这一点也是第一种方法和第二种方法的根本不同之处,我们接着看第二种方法:
第二种方法: 集合名.discard(元素)
第二种方法和第一种基本一致,唯一区别即是当使用 集合名.discard(元素) 删除元素时,若试图删除一个目标集合中原本不存在的集合,并不会报错从而影响下面语句的运行:
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> shengci.remove('what')
>>> shengci
{'banana','pencil','house','home','sarah','ruler'}
我们发现当我们想要删除的元素原本就不存在于那个集合时,原集合既没有发生改变,也没有出现任何报错。
第三种方法: 集合名.pop()
老师准备从某天开始每天从生词表中抽出一个词来讲,然后在生词表中将这个词删除。于是用到了 集合名.pop() :
>>> shengci = {'banana','pencil','house','sarah','ruler'}
>>> shengci.pop()
>>> house
>>> shengci
{'banana','pencil','sarah','ruler'}
6.6 检查元素
第一种方式: 元素 in 集合 或 元素 not in 集合
这时候我们再用 元素 in 集合 和 元素 not in 集合 的方式检查一下刚才删掉的词还在不在:
>>> shengci = {'banana','pencil','sarah','ruler'}
>>> 'home' in shengci
False
>>> 'house' not in shengci
True
第二种方式: 集合名.issuperset(集合名)
刚才那是检查某个元素在或者不在某个集合中,我们还可以用 集合名.issuperset(集合名) 的方式检验另一个集合中的每一个元素是不是都在集合中。若前者集合包含了后者集合中的每一个元素则返回 True ,若前者集合没有包含后者集合中的每一个元素则返回 False :
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> likangyi = {'banana','pencil','house'}
>>> shengci.issuperset(likangyi)
True
注意两点:顺序问题:检查前面集合是否包含了后面集合中的任一元素
但凡有任一元素不在,即会报错。
第三种方式: 集合名.issubset(集合名)
这和第二种方式很像,判断一个集合中的每一个元素是不是都在另一个集合中。若前者集合中的每一个元素都在后者内部则输出 False ,若前者集合中的元素有任一不包含在后者内部则输出 False :
>>> shengci = {'banana','pencil','house','home','sarah','ruler'}
>>> likangyi = {'banana','pencil','house'}
>>> likangyi.issuperset(shengci)
True
注意两点:顺序问题:检查前面集合中的任一元素是不是都在后面的集合中
但凡有任一元素不在,即会报错。
第三种方式与第二种方式中的判断位置完全相反。
第三种方式: 集合名.isdisjoint(集合名)
这种方法主要判断两个集合中是否具有共同元素,有共同元素则返回 True ,没有共同元素则返回 False 。比如桃老师想判断 '李康怡' 小朋友和 '王有才' 同学有没有共同都写错的词:
>>> likangyi = {'banana','pencil','house'}
>>> wangyoucai = {'pencil','home','sarah'}
>>> likangyi.isdisjoint(wangyoucai)
True
结果为真,即二者内部存在共同元素。
注意:该方法只可判断是否具有共同元素,不能输出具体的元素内容。
6.7 计数
可见返回了 house 这个词,再输出一遍发现原词表中已经没有这个词了。这时候我们再用 len(集合名) :
>>> len(shengci)
4
可见生词表中还有四个单词。
6.8 清空元素
最终经过了大几轮的学习与听写,生词表内的词全班都已经不会再错了。老师准备暂时用 元素名.clear() 的方式清空这个生词表:
>>> shengci = {'banana','pencil','sarah','ruler'}
>>> shengci.clear()
>>> shengci
set()
可见返回结果已经是一个空值了。
6.9 几组用法的区别分类:
想必看到现在的你感觉脑子有点乱,作为作者的我也有这种感觉…主要问题是这一章的很多功能都有至少3个方式来实现,而且不同的用法之间效果也都非常相似。
(就是不承认是我写的不好!!!!)
如果你看了也感觉有点乱,一时还没有消化的话,下面我将梳理一下这篇文章的一些可能容易让你抓狂的点:
集合的集中常见关系整理:
刚才经常出现的 ……_update 是啥?
可以把这个 …update 看作为 更新为…,凡是带这三个字就要明白:结果已经作为新元素更新到原来的集合里面了,而不会再单独输出结果了。因为更新的是一个值,所以也是可以再次赋值的。上述中牵扯到 …update 和 …… (带不带update) 区别的,都可以从这个思路来理解。