2.2 集合可以去除重复元素
Python语言中的集合(Set)是无法包含重复元素的,所以在实际应用中,经常通过集合来执行去重操作。此外,在数据分析等应用场景中,还可以使用集合包含的各种操作方法,比如union(并集)、intersection(交集)和difference(差集)。
2.2.1 通过集合去掉重复的元素
在下面的SetRemoveDup.py范例程序中演示了集合的常见用法,可以从中看到集合具有自动去掉重复元素的功能。
1 # !/usr/bin/env python 2 # coding=utf-8 3 # 调用集合的方法把列表转换成集合 4 set1=set(["a", "a", "b", "b", "c"]) 5 print(set1) # 输出set(['a', 'c', 'b']) 6 # 添加元素 7 set1.add("d") 8 set1.add("c") # 由于重复,因此无法添加 9 print(set1) # set(['a', 'c', 'b', 'd']) 10 11 set2=set1.copy() 12 set1.clear() 13 print(set1) # 由于已清空,因此输出set([]) 14 print(set2) # set(['a', 'c', 'b', 'd']) 15 16 set2.discard("f") # 删除元素,哪怕没找到也不会抛出异常 17 18 list=[1,1,2,2,3,3,4,4,5] # 含重复元素的列表 19 setFromList=set(list) # 通过集合去掉重复的元素 20 print(setFromList) # 输出为set([1, 2, 3, 4, 5])
在第4行中通过调用set方法,把一组包含重复字母的列表转换成集合元素,从第5行的打印语句可知,在set1中,已经没有重复元素了,由此能体会到集合的去除重复元素的特性。
在第7行和第8行中,调用add方法向set1中添加元素,由于已经有了c这个字母,因此无法再插入重复的元素。在第11行和第12行中,可以看到常用于集合的copy和clear方法。
在第16行中,调用discard方法来去掉集合中的元素,该方法也适用于列表(List)。与之前提到的remove方法不同,调用这个方法删除元素时,哪怕元素不存在,也不会抛出异常。
在第18行和第19行中演示了集合的常规做法,即去掉列表中的重复元素。和第4行程序语句去重复功能不同的是,这里是通过列表保存了一份去重复前的原始数据,这样哪怕对去重复后的数据操作有误,也能通过原始数据进行恢复。
2.2.2 常见的集合操作方法
在数据统计和分析场景里,经常会对不同的对象进行各种集合操作,比如交集、并集和差集等,通过集合提供的方法,可以比较便捷地实现这一功能。在下面的SetHandleData.py范例程序中演示了集合的各种操作。
1 # !/usr/bin/env python 2 # coding=utf-8 3 # 以大括号的方式定义集合 4 set1={'1', '3', '5', '7'} 5 set2={'2', '3', '6', '7'} 6 # 不能用中括号的方式定义集合,例如 set1=['1', '3', '5', '7'] 7 # 交集 8 set3=set1 & set2 9 print(set3) # 输出 set(['3', '7']) 10 print(set1 & set2) # 输出 set(['3', '7']) 11 print(set1.intersection(set2)) # 输出 set(['3', '7']) 12 # 并集 13 set4=set1 | set2 14 print(set4) # 输出set(['1', '3', '2', '5', '7', '6']) 15 print(set1 | set2) # set(['1', '3', '2', '5', '7', '6']) 16 print(set1.union(set2)) # set(['1', '3', '2', '5', '7', '6']) 17 # 差集 18 print(set1 - set2) # 输出set(['1', '5']) 19 print(set1.difference(set2)) # 输出set(['1', '5']) 20 print(set2 - set1) # 输出set(['2', '6']) 21 print(set2.difference(set1)) # 输出set(['2', '6']) 22 # 演示不可变集合的特性 23 unChangedSet=frozenset(3.14,9.8) 24 # unChangedSet.add(2.718) 25 # unChangedSet[0]=2.718 26 # unChangedSet.discard(3.14)
之前提到,定义列表时用方括号,定义元组时用小括号,定义集合对象时,要像第4行和第5行中那样通过大括号。这里请注意,如果像第6行那样,通过方括号定义的是列表,那么列表类对象是无法参与后面的各种针对集合的操作的。
从第8行到第11行的程序语句演示了集合交集的操作,具体可以像第8行和第10行程序语句那样使用“&”运算符,也可以像第11行那样通过调用intersection方法来求交集。通过打印语句可以看到,返回的结果是set1和set2中都有的元素,即交集,由此能验证求交集的结果。而从第13行到第16行的程序语句是使用“|”运算符和调用union方法来求并集。
从第18行到第21行的程序语句,是使用“-”运算符和调用difference方法来求两个集合的差集,即返回在第一个集合中有且在第二个集合中没有的元素。
在前文中介绍过,可以用元组来保证列表元素的不可操作性,在上面范例程序的第23行中通过调用frozenset来保证集合元素的不可操作性。如果去掉第24行到第26行的注释,就会发现无法通过程序代码来修改frozenset类型的unChangedSet中的元素,由此可以验证这种方式实现的列表的元素的“不可操作性”。
2.2.3 通过覆盖sort定义排序逻辑
在集合(以及之前的列表)中,都可以通过调用sort方法对集合内的元素进行排序。在默认的情况下,sort方法是会按升序的方式排序集合中的元素,这里就涉及一个问题,程序员在开发时如何定义排序的标准?比如如何把排序的标准设置为“降序”?
在下面的SetSortDemo.py范例程序中,演示了列表降序排列的方法,具体做法是,在调用sort方法时,传入了“定义排序规则”的desc方法。
1 # !/usr/bin/env python 2 # coding=utf-8 3 # 定义降序规则 4 def desc(x, y): 5 if x < y: 6 return 1 # 如果x小于y,则x排在y之前 7 elif x > y: 8 return -1 # 如果x大于y,则x排在y之后 9 else: 10 return 0 # 否则并列 11 # 定义待排序的numbers列表 12 numbers=[5, 58, 47 ,75 ,100] 13 numbers.sort(desc) # 在排序时用到desc方法 14 print numbers # 输出[100, 75, 58, 47, 5] 15 numbers.sort() 16 print numbers # 输出[5, 47, 58, 75, 100]
在第13行中通过调用sort方法对numbers列表进行排序,与之前不同的是,这里传入了desc方法,用来定义排序的规则。
第4行到第10行的程序语句定义desc方法时,定义的排序规则是:如果x小于y,则返回1,即x排在y之前;如果大于,则返回-1,x排在y之后;如果相等则返回0,即两数并列。在第14行和第16行中,打印了以不同方式排序后的列表,从输出结果上来看,分别实现了降序和升序的排序。
其实,这里定义排序规则和在sort方法里通过参数传入规则的代码都不复杂,但请大家熟悉这种“通过传入参数定义规则”的编写程序的方式,这种定义排序规则的做法在实际项目中会经常用到。