其他的数字类型——Sets——为什么是sets?
Set 操作有许多常见用处,一些比数学上的用处更实用。比如,因为项在 set 中只保存一次,因此 set 可以被用来从其他集合中过滤重复值,然而在这个过程中项可能会被重新排序,因为 set 通常是无序的。简单地将集合转为 set,然后再将它转换回来(这里 set 在 list
函数调用中可用是因为它们是可迭代的,这是另一个将在稍后讨论的技术细节):
>>> L = [1, 2, 1, 3, 2, 4, 5]
>>> set(L)
{1, 2, 3, 4, 5}
>>> L = list(set(L)) # 去重
>>> L
[1, 2, 3, 4, 5]
>>> list(set(['yy', 'cc', 'aa', 'xx', 'dd', 'aa'])) # 但顺序可能改变
['cc', 'xx', 'yy', 'dd', 'aa']
Set 还能被用来找出列表,字符串和其他可迭代对象的差异 —— 简单地转换为 set 然后得到差集 —— 然而(再一次)set 的无序特性意味着结果可能和原来的不匹配。下面最后两个例子比较了在 3.X 中字符串对象类型的属性列表(在 2.7 中的结果是不同的):
>>> set([1, 3, 5, 7]) - set([1, 2, 4, 5, 6]) # 找出列表差异
{3, 7}
>>> set('abcdefg') - set('abdghij') # 找出字符串差异
{'c', 'e', 'f'}
>>> set('spam') - set(['h', 'a', 'm']) # 找出混合类型的差异
{'p', 's'}
>>> set(dir(bytes)) - set(dir(bytearray)) # 在bytes类型但不在bytearray类型中
{'__getnewargs__'}
>>> set(dir(bytearray)) - set(dir(bytes))
{'append', 'copy', '__alloc__', '__imul__', 'remove', 'pop', 'insert',
...更多...]
还可以使用 set 来执行顺序无关的相等测试(在测试前转为 set),因为顺序在 set 中不重要。更规范地说,当且仅当每个 set 中的每个元素都包含在另一个 set 中,那么两个 set 才是相等的 —— 也就是说,每个 set 是另一个的子集,不管顺序。比如,可以这样来比较本应该一样但以不同顺序产生的不同程序输出。对相等测试,测试前排序会得到相同的效果,但 set 不依赖于昂贵的排序操作,且该操作对它们的结果进行排序来支持额外的 set 不支持的数值测试(大于,小于等等):
>>> L1, L2 = [1, 3, 5, 2, 4], [2, 5, 3, 4, 1]
>>> L1 == L2 # 序列中顺序很重要
False
>>> set(L1) == set(L2) # 排序无关的相等
True
>>> sorted(L1) == sorted(L2) # 相似,但将结果排序
True
>>> 'spam' == 'asmp', set('spam') == set('asmp'), sorted('spam') ==
sorted('asmp')
(False, True, True)
Set 还可以在遍历图表或其他循环结构时用来跟踪已到达的位置。比如,在第 25 章和第 31 章中将分别学习的传递模块重载器和继承树列表的例子(必须跟踪已访问的项来避免循环),如在第 19 章的摘要中所讨论的。在这个上下文中使用列表是低效的,因为搜索需要线性扫描。虽然记录已访问的状态作为字典的键是高效的,但 set 提供了本质上等价的可选方案(而且可能更或更不直观,这取决于你问谁)。
最后,当处理大数据集时(比如,数据库查询结果)set 也很方便 —— 两个 set 的交集包含两个类别都有的对象,并集则包含在每个 set 中的所有项。下面是一个有些现实意义的 set 操作实例,应用在一个假想公司中的人名列表,使用 3.X/2.7 的 set 字面量和 3.X 结果显示(在 2.6 和之前版本中使用 set
函数):
>>> engineers = {'bob', 'sue', 'ann', 'vic'}
>>> managers = {'tom', 'sue'}
>>> 'bob' in engineers # bob是工程师吗?
True
>>> engineers & managers # 谁既是工程师又是经理?
{'sue'}
>>> engineers | managers # 在每个类别中的所有人
{'bob', 'tom', 'sue', 'vic', 'ann'}
>>> engineers - managers # 不是经理的工程师
{'vic', 'ann', 'bob'}
>>> managers - engineers # 不是工程师的经理
{'tom'}
>>> engineers > managers # 所有的经理都是工程师吗? (超集)
False
>>> {'bob', 'sue'} < engineers # 他们两个都是工程师吗? (子集)
True
>>> (managers | engineers) > managers # 所有人是经理的超集
True
>>> managers engineers # 谁在其中一个类别但不在两个?
{'tom', 'vic', 'ann', 'bob'}
>>> (managers | engineers) - (managers engineers) # 交集!
{'sue'}
在 Python 库手册和一些数学和关系式数据库理论的书籍中,可以找到 set 操作的更多细节。而且,在第 8 章,Python 3.X 的字典视图对象的上下文中,会对这里已经见过的一些 set 操作进行重新介绍,请保持关注。
推荐文章: