本节是第五讲的第六小节,上节为大家介绍了Python语言常用的元组与列表,本节将为大家介绍另一种组合数据类型-集合。
集合类型(Set Types)
set也是一种组合数据类型,支持成员关系操作符(in)、对象大小计算操作符 (len()),并且也是iterable(可迭代)。此外,集合数据类型至少提供一个set.isdisjoint()方法,支持比较,也支持位逻辑操作符(在集合用于联合、交叉等上下文中使用)。Python提供了两种内置的集合类型:可变的set类型,固定的frozenset类型。进行迭代时,集合类型以任意顺序提供其数据项。
只有可哈希运算的对象可以添加到集合中,可哈希运算的对象包含一个__hash__() 特殊方法,其返回值在某个对象的整个生命周期内都是相同的,并可以使用__eq__() 特殊方法进行相等性比较(特殊方法——方法名的起始与结尾都使用两个下划线)。
所有内置的固定数据类型(比如float、frozenset、int、str、tuple)都是可哈希运算的,都可以添加到集合中。内置的可变数据类型(比如dict、list、 set)都不是可哈希运算的,因为其哈希值会随着包含项数的变化而变化,因此,这些数据类型不能添加到集合中。
集合类型可以使用标准的比较操作符(<、<=、==、!=、>=、>)进行比较,要注意的是,操作符==与!=都使用的是其通常的含义,其比较的方式是逐项比较(对嵌套项,比如集合内的元组或固定集合,则递归比较),其他比较操作符则进行子集比较或超集比较,稍后将进行讲述。
集合(Sets)
集合是0个或多个对象引用的无序组合,这些对象引用所引用的对象都是可哈希运算的。集合是可变的,因此可以很容易地添加或移除数据项,但由于其中的项是无序的,因此,没有索引位置的概念,也不能分片或按步距分片。
如果给出了代码snippet创建的集合:
S = {7, "veil", 0, -29, ("x", 11), "sun", frozenset({8, 4, 7}), 913}
set数据类型可以作为函数进行调用,set()——不带参数进行调用时将返回一个空集合;带一个set参数时返回该参数的浅拷贝;对于任意其他参数,则尝试将给定的对象转换为集合。该函数只接受一个参数的情况。非空集合也可以不使用set()函数创建, 空集合必须使用set()创建,而不能使用空的圆括号来创建。包含一个或多个项的集合可以使用逗号分隔的数据项(包含在圆括号中)序列来创建,另一种创建集合的方法是使用集合内涵——在后面将讲述这一主题。
集合中包含的每个数据项都是独一无二的——添加重复的数据项固然不会引发问 题,但是也毫无意义。比如,下面产生的三个集合都是一样的:set("apple")、set("aple")、 {'e','p','a','l'}鉴于此,集合常用于删除重复的数据项。比如:x是一个字符串列表, 在执行x = list(set(x))之后,x中的每个字符串都将是独一无二的——其存放顺序也是任意的。
集合支持内置的len()函数,也支持使用in与not in进行的快速成员关系测试。此外,集合还提供了通常的集合操作符,如图所示。
下表给出了集合方法与操作符的完整列表。所有“update”方法(set.update()、set.intersection_update()等)都可以接受任意的iterable作为参数——但等价的操作符版本(|=、&=等)则要求两边的操作数都已设置。
语法 描述
s.add(x) 将数据项X添加到集合S中一如果S中尚未包含X
s.clear() 移除集合S中的所有数据项
s.copy() 返回集合S的浅拷贝※
s.difference(t) s - t 返回一个新集合,其中包含在s中但不在集合t中的所有数据项※
s.difference_update(t) s -= t 移除每一个在集合t但不在集合s中的项
s.discard(x) 如果数据项x存在于集合s中,就移除该数据项,参见set.remove()
s.intersection(t) s & t 返回一个新集合,其中包含所有同时包含在集合t与s中的数据项※
s.intersection_update(t) s &= t 使得集合S包含自身与集合t的交集
s.isdisjoint(t) 如果集合s与t没有相同的项,就返回True※
s.issubset(t) s<=t 如果集合s与集合t相同,或者是t的子集,就返回True。使用s<t可以 测试s是否是t的真子集※
s.issuperset(t) s >=t 如果集合s与集合t相同,或者是t的超集,就返回True。使用s>t可以测试t是否是s的真子集※
s.pop() 返回并移除集合s中一个随机项,如果s为空集,就产生KeyError异常
s.remove(x) 从集合s中移除数据项x,如果s中不包含x,就产生KeyError异常,参 见 set.discard()
s.symmetric_difference(t) s ^ t 返回一个新集合,其中包含s与t中的每个数据项,但不包含同时在这两个集合中的数据项※
s.symmetric_difference_update(t) s^=t 使得集合S只包含其自身与集合t的对称差
s.union(t) s | t 返回一个新集合,其中包含集合s中的所有数据项以及在t中而不在S中的数据项※
s.update(t) s |= t 将集合t中每个S中不包含的数据项添加到集合S中
※这一方法及其操作符(如果有)也可用于frozensets。
集合数据类型的一个常用场景是进行快速的成员关系测试。比如,如果用户没有输入任何命令行参数,或输入的参数是"-h"或"--help",就返回给用户一条使用帮助消息:
if len(sys.argv) == 1 or sys.argv[1] in {"-h", "--help"}:
另一个常用的场景是确保没有处理重复的数据。比如,假定有一个iterable (比如一个列表),其中包含的是来自Web服务器日志文件的IP地址,我们需要对其进行适当处理,每次针对一个IP地址。假定IP地址是可哈希运算的,存放在iterable ips中, 我们需要为每个IP地址调用的函数称为process_ip(),并且已进行定义。
#下面的代码段将完成我们的任务,尽管会有细微的不同:
seen = set()
for ip in ips:
if ip not in seen:
seen.add(ip)
process_ip(ip)
for ip in set(ips):
process_ip(ip)
在左边的代码段中,如果以前尚未处理某个IP地址,就将其添加到集合seen中, 并对其进行处理,否则忽略该IP地址。在右边的代码段中,则首先获取每个唯一的地址进行处理。两边代码段的差别在于:第一,左边的代码段创建了一个集合seen,而 右边的代码段不需要创建;第二,左边的代码段处理IP地址的顺序是按照该IP地址在ips这一 iterable中的出现顺序,而右边代码段处理IP地址的顺序是随机的。右边的方法更易于编写代码,但是如果ips iterable中的IP地址顺序很重要,就必须使用左边的方法,或者将右边代码段的第一行代码修改为类似于for ip in sorted(set(ips))的形式 如果这一形式足以获取需要的顺序。理论上,如果ips中的IP地址项数非常大,那么右边的方法会比较慢,因为该方法一次性创建集合,而不是递增地创建。
集合也可用于删除不需要的数据项,比如,有一个文件名列表,但是不希望其中包含任何makefiles (或许因为其是生成的,而非自己编写的),则可以使用如下代码:
filenames = set(filenames)
for makefile in {"MAKEFILE","Makefile”,"makefile“}:
filenames.discard(makefile)
上面的代码将移除列表中的任何makefile (使用任何标准的大写化形式),如果 filenames列表中不包含makefile,则不进行任何实际处理。通过集合差别操作符(-), 可以在一行代码中实现同样的功能:
filenames = set(filenames) - {"MAKEFILE", "Makefile", "makefile"}
我们也可以使用set.remove()方法移除数据项,如果要移除的数据项并不在集合 中,这一方法会产生KeyError异常。
集合内涵(Set Comprehensions)
除调用set()创建集合,或使用集合字面值创建集合外,我们可以使用集合内涵来创建集合。集合内涵是一个表达式,也是一个带有可选条件(包含在花括号中)的循 环,与列表内涵类似,也支持两种语法格式:
{expression for item in iterable}
{expression for item in iterable if condition}
#我们可以使用上面的语法进行过滤(假定顺序不重要),下面给出一个实例:
html = {x for x in files if x.lower().endswith((".htm",".html"))}
给定files中的一个文件名列表,上面这一集合内涵使得集合html只存放那些 以.htm或.html结尾的文件名,这里不区分大小写。
就像列表内涵一样,集合内涵中使用的iterable本身也可以是一个集合内涵(或任何其他类型的内涵),因此,可以创建相当复杂的集合内涵。
固定集合(Frozen Sets)
固定集合是指那种一旦创建后就不能改变的集合,当然,我们可以将绑定到固定集合的对象引用重新引用其他对象。固定集合只能使用frozenset数据类型函数(作为函数进行调用)进行创建,不带参数调用时,frozenset()将返回一个空的固定集合,带一个frozenset参数时,将返回该参数的浅拷贝,对任何其他类型的参数,都尝试将给定的对象转换为一个frozenset。该函数只能接受一个参数。
由于固定集合是固定不变的,因此其支持的方法与操作符所产生的结果不能影响固定集合本身。表3-2列出了集合支持的所有方法 固定集合支持frozenset.copy()、frozenset.difference()(-)、 frozenset.intersection()(&)、 frozenset.isdisjoint(), frozenset. issubset() (<=,<则用于真子集)、frozenset.issuperset() (>=, >则用于真子集)、frozenset. union()(|)以及frozenset.symmetric_difiference() (^),所有这些方法在该表中都以图标※ 指明。
如果将二元运算符应用于集合与固定集合,那么产生结果的数据类型与左边操作数的数据类型一致。因此,如果f是一个固定集合,s是一个集合,那么f&s将产生 一个固定集合,s & f则产生一个集合。在使用==与!=等操作符时,操作数的顺序则无关紧要,如果两个集合包含相同的项,那么f==s结果为True。
由于固定集合的固定不变性,使得其满足集合项的可哈希运算的标准,因此,集合与固定集合都可以包含固定集合。
以上内容部分摘自视频课程05后端编程Python-6集合类型,更多实操示例请参照视频讲解。跟着张员外讲编程,学习更轻松,不花钱还能学习真本领。