本章我们将学习如何使用Python的组合数据类型将数据项集合在一起,以便在程序设计时有更多的选项。
组合数据类型
Python提供了5中内置的序列类型:bytearray、bytes、list、str与tuple,序列类型支持成员关系操作符(in)、大小计算函数(len())、分片([]),并且是可可迭代的。
元组是个有序序列,包含0个或多个对象引用,使用小括号包裹。元组是固定的,不能替换或删除其中包含的任意数据项。
使用()创建一个元组:
也可以使用tuple()创建一个元组:
语法
描述
tup[1]
读取第二个元素
tup[-2]
反向读取;读取倒数第二个元素
tup[1:]
截取元素
元组只提供两种方法:
语法
描述
返回对象x在元祖t中出现的次数
返回对象x在元组t中出现的最左边位置
与字符串一样,元组之间可以使用 + 号和 * 号进行运算。这就意味着他们可以组合和复制,运算后会生成一个新的元组。
语法
描述
len(t)
返回元组t中元素个数
连接
复制
in
元素是否存
for … in …:
迭代
比较运算符<、<=、>、>=、==、!=
逐项进行比较
元组中的元素值是不允许删除的,但我们可以使用del删除整个元组:
当元组出现在二进制操作符的左边或出现在unary语句的右边时,可以不使用圆括号。
命名的元组(namedtuple)与普通元组一样,有相同的表现特征,其添加的功能就是可以根据名称引用元组中的项。
collections模块提供了namedtuple()函数,用于创建自定义的元组数据类型。该函数的第一个参数是想要创建的自定义元组数据类型的名称,第二个参数是一个字符串,其中包含使用空格分隔的名称,每个名称代表该元祖数据类型中的一项。该函数返回一个自定义的类,可用于创建命名的元组。
这里我们创建了包含两个Sale项的列表,我们可以使用索引位置来引用元组中的项,也可以使用名称进行引用,后者正式命名的元组的特点:
列表是包含0个或多个对象引用的有序序列,支持与字符串以及元组一样的分片与步距语法,列表是可变的,因此我们可以对列表中的项进行删除或替换,插入、替换或删除列表中的分片也是可能的。
使用[]创建一个元组:
也可以使用list()创建一个列表:
语法
描述
lst[1]
读取第二个元素
lst[-2]
反向读取;读取倒数第二个元素
lst[1:]
截取元素
下表中,L为列表。
语法
描述
将数据项x追加到L的末尾
统计元素x在L中出现的次数
将iterable m的项追加到L的末尾
返回数据项x在L中(或L的start: end分片中)最左边出现的索引位置,如果没找到x,则产生ValueError异常
在索引位置i处插入元素x
移除L最右边的数据项,并返回该元素的值
移除L索引位置i处的数据项,并返回该元素的值
从L中移除最左边的数据项x,如果没找到x产生ValueError异常
对L进行反转
对L进行排序,与内置的sorted()函数一样,可以接受可选的key与reverse参数
任意可迭代的(列表、元组等)数据类型都可以使用序列拆分操作符进行拆分,即*。用于赋值操作符左边的两个或多个变量时,其中一个使用*进行引导,数据项将赋值给该变量,而所有剩下的数据项将给带星号的变量。
由于列表是可变的,我们可以对其数据项进行删除。
列表内涵是一个表达式,也是一个循环,该循环有一个可选的、包含在方括号中的条件,作用是为列表生成数据项,并且可以使用条件过滤掉不需要的数据项,可以使用表达式,也可以使用附加条件。常见语法:
在没有列表内涵时,我们找出1900~1940年之间所有的闰年,可能会这么写:
学习了列表内涵之后我们可以简化程序:
两种方法等效,得到同样的结果。
set也是一种组合数据类型,支持成员关系操作符(in)、对象大小计算操作符(len()),并且也是iterable。Python提供了两种内置的集合类型:可变的set类型,固定的frozenset类型。进行迭代时,集合类型以任意顺序提供其数据项。
只有可哈希运算的对象可以添加到集合中。所有的内置固定数据类型(比如float、frozenset、int、str、tuple)都是可哈希运算的,可以添加到集合中。内置的可变数据类型(比如dict、list、set)都不是可哈希运算的,不能添加到集合中。
集合是0个或多个对象引用的无序组合。集合是可变的,因此可以很容易的添加和移除数据项,但是由于其中的项是无序的,因此没有索引位置的概念,也不能分片或按步距分片。
使用set()创建一个集合:
集合中包含的每个数据项都是独一无二的——添加重复的数据项固然不会引发问题,但是也毫无意义。比如,下面产生的三个集合是一样的:set('apple')、set('aple')、{‘e', 'p', 'l', 'a'}。鉴于此,集合常用于删除重复的数据项。比如,x是一个字符串列表,在执行x=list(set(x))之后,x中的每个字符串都是独一无二的,存放顺序是任意的。
s、t为集合,x为数据项。
语法
描述
将x添加到s中——如果s中尚未包含x
清空s
返回s的浅拷贝
返回一个新集合,其中包含在s中但不在t中的所有数据项
移除每一个在t中但不在s中的项
如果x在s中,则移除x
返回一个新集合,其中包含所有同时包含在s和t中的数据项
使得s包含自身与t的交集
如果s与t没有相同的项,返回True
如果s与t相同,或s是t的子集,返回True;使用s<t可以测试s是否是t的真子集
如果s与t相同,或s是t的超集,返回True
返回并移除s中的一个随机项,如果s为空,就产生一个KeyError
从s中移除x,如果s中不包含x,就产生KeyError
返回一个新集合,其中包含s与t中的每个数据项,但不包含同时在这两个集合中的数据项
使得s只包含其自身与t的对称差
返回一个新集合,其中包含集合s中的所有数据项以及在t中而不在s中的数据项
将t中每个s中不包含的数据项添加到集合s中
除了调用set()创建集合,或使用集合字面值创建集合外,我们可以使用集合内涵创建集合。集合内涵是一个表达式,也是一个带有可选条件的循环,支持的语法:
固定集合是指那种一旦创建就不能修改的集合,只能使用frozenset数据类型函数创建,不带参数调用时,frozenset()返回一个空的固定集合,带一个frozenset参数时,将返回改参数的 浅拷贝,对于任何其他类型的参数,都尝试将给定的对象转换为一个forzenset。
dict是一种无序的组合数据类型,其中包含0个或多个键-值对。
可以使用{}创建:
也可以使用dict()函数创建:
字典的键值是独一无二的,因此,如果向字典中添加一个已存在的键值项,实际效果是新值替换旧值。
d为字典
语法
描述
移除d中所有项
返回d的浅拷贝
返回一个dict,该字典的键为序列s中的项,值为None或V
返回键k关联的值,如果d中不存在k则返回None
返回键k关联的值,如果d中不存在k则返回v
返回d中所有(key, value)对的视图
返回d中所有键的视图
返回键k的关联值,并移除键为k的项,如果k不包含在d中就产生KeyError
返回键k的关联值,并移除键为k的项,如果k不包含在d中就返回v
返回并移除d中任意一个(key, value)对,如果d为空就产生KeyError
将a中每一个尚未包含在d中的(key, value)对添加到d中,对同时包含在d与a中的每个键,使用a中对应的值替换d中对应的值——a可以是字典,也可以是(key, value)对的一个iterable或关键字参数
返回d的所有值的视图
上面提到了“视图”概念,其相对于通常的iterables有两个不同点:
注:两种通过键取值方式的比较
字典内涵是一个表达式,也是一个循环,该循环带有一个可选条件。语法:
例:
默认字典也是一种字典——这种字典包含普通字典所提供的所有操作符与方法,与其不同的是可以对遗失的键进行处理。
创建默认字典时,我们可以传入一个工厂函数,这样就会为遗失的键创建默认值。看下面例子
上面我们创建的默认字典words永远不会产生KeyError异常,如果遇到没有的键,其值通过工厂函数(int())设置为0。
可以看出我们通过二元组列表创建有序字典后,获取去键视图也为有序的。
iterable数据类型每次返回其中的一个数据项。任意包含__iter__() 方法的对象或任意序列(也即包含__getitem__()方法的对象)都是一个iterable,并可以提供一个迭代子。迭代子是一个对象,该对象可以提供__next__()方法,该方法依次返回每个相继的数据项,并在没有数据项时产生StopIteration异常。
常见的迭代操作符与函数(s与t为序列):
语法
描述
s+t
返回一个序列,该序列是s与t的连接
s*n
返回一个序列,该序列是s的n个副本的连接
x in i
如果x出现在iterable i中,返回True
all(i)
如果iterable i中的每一项都评估为True,就返回True
any(i)
如果iterable i中的任意项评估为True,就返回True
emumerate(i, start)
通常用于for… in 循环中,提供一个(index, item)元组序列,其中索引其实值为0或start
len(x)
返回x的“长度”
max(i, key)
返回iterable i中的最大的项,如果给定的是key函数,就返回key(item)值的最大项
min(i, key)
返回iterable i中的最小的项,如果给定的是key函数,就返回key(item)值的最小项
range(start, stop, step)
返回一个整数迭代子,使用一个参数(stop)时,迭代子的取值范围从0到stop-1;使用两个参数(start与stop)时,迭代子取值范围从start到stop-1;使用三个参数时,迭代子取值范围从start到stop-1,每两个值之间间隔step
reversed(i)
返回一个迭代子,该迭代子以反序从迭代子i中的返回项
sorted(i, key, reverse)
以排序后顺序从迭代子i返回项,key用于提供DSU(修饰、排序、反修饰)排序,如果reverse为True,则排序以反序进行
sum(i, start)
返回iterable i中项的和,加上start(默认为0),i可以包含字符串
zip(i1, …, iN)
返回元组的迭代子,使用迭代子i1到iN
数据项返回的顺序依赖于底层的iterable。对列表和元组等情况,数据项的返回值通常从第一个数据项开始依次返回,而对于字典与集合,迭代子是任意顺序的返回项。
由于数据片总是曲子某个数据项的一个单独副本,所以获取一个列表的副本可以通过下面方式:
在以上各种组合数据类型创建的时候,提到可以使用工厂方法来创建一个组合数据类型的副本:
注意:以上的复制都是浅拷贝,也就是说,复制的只是对象引用而非对象本身。对于固定数据类型(数字、字符串等),这与复制的效果是相同的,但对于可变的数据类型,比如嵌套的组合类型,这意味着相关对象同时被原来的组合与复制得来的组合引用。请看下面代码:
从输出结果可以看出,前两项固定数据类型并没有同时改变,而列表中的列表同时变化,说明x与y的第三项都指向的同一列表的引用。我们可以使用深拷贝来避免此类问题: