一、Python 标准库的序列类型分为:
其中,容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列存放的是值而不是引用。换句话说,扁平序列是一段连续的内存空间,更加紧凑,但其内只能存放如数值、字符和字节等基础类型。
二、而依据序列能否被修改,还能分为:
序列可不可变指的是序列的大小与其存储的值可不可变。如元组,虽然不可变,但其可变对象元素是可变的,因元组里存储的“值”是引用,即该元素指向哪个对象是不可变的,但指向的对象本身是允许改变的。
列表推导用于便捷地构建列表,生成器表达式可用于构建任何类型的序列。
基本形式:[ obj for x1 in iter1 for x2 in iter2 for .... ]
,其中obj
为使用x1
、x2
、…表示的对象,可以为元组、字典或可调用对象等,iter1
、iter2
、…为可迭代对象,用于产出值。最终的结果为由obj
对象实例构成的数组。
列表推导只用于生成列表,若要构建其他类型的序列,则需要使用生成器表达式。
生成器表达式可用于构建任意类型的序列(将生成器表达式传入对应的构造函数),生成器表达式的语法跟列表推导差不多,只不过将[]
换为()
。
dict((x,y) for x in range(3) for y in "ABC")
>> {1: 'C', 2: 'C', 3: 'C'}
且若生成器表达式为一个函数调用中的唯一参数,则不需要再额外使用括号。
列表推导会一次性生成含有 n 个元素列表,受限于内存,列表推导存在内存不足的风险。
而生成器表达式只在每次for
循环运行时才生成一个组合,省去运行时的内存开销。故在初始化除列表之外的序列,使用生成器表达式能够省去额外的内存占用。
句法提示:Python会忽略
[]
、{}
、()
中的换行,在其内可以省略续行符\
。
把元组用作记录:元组其实是对数据的记录,元组中元素的信息蕴含了一个字段的数据与该字段的位置。将元组当作字段的集合能够充分利用元组的数量与位置信息。
如:
city,year,pop,chg,area = ("Tokyo",2003,32450,0.66,8014)
以上将元组用作一条数据的记录,充分利用了元组的记录数据与记录位置功能。
应用:
用例:
# 交换两个变量的值
a,b = b,a
使用元组拆包可以将可迭代对象拆开作为函数的多个参数:
t = (2,3)
add(*t)
>> 5
也可以借助元组拆包从函数中返回多个值:
# func为返回长度为3的元组的函数
a,b,c = func()
元组拆包可以用于任何可迭代对象上,前提是平行赋值时对应的元素数量一致,除非用*
来收集多余元素。其中*
前缀只能用于一个变量名前,但是该变量可以出现在赋值表达式的任意位置。
a,b,*rest = (1,2,3,4,5,6,7)
# a=1, b=2, rest=[3,4,5,6,7]
a,*rest,b = (1,2,3,4,5,6,7)
# a=1, rest=[2,3,4,5,6], b=7
同时也可以使用占位符_
(本质上也是一个变量)来处理拆包时不需要的数据。
接收表达式的元组可以是嵌套式的,只要这个接收元组的嵌套结构符合表达式的嵌套结构,Python 就可做出正确对应:
(a,(b,c),d)=(1,(2,3),4)
# a=1, b=2, c=3, d=4
collections.namedtuple
是一个工厂函数,其返回一个类并构建一个带名字的元组:
from collections import namedtuple
City = namedtuple('city','name country population')
# 构建City类,该类的实例为一个名为city的元组,且元素中每个元素都有对应的名字
tokyo = City("Tokyo","JP","36,933")
tokyo
>> city("Tokyo","JP","36,933")
tokyo.country
>> "JP"
构建一个具名元组类需要两个参数:一个是元组名,另一个是类的各个对应字段的名字。后者可以是数个字符串组成的可迭代对象,也可以是由空格分开的字段名组成的字符串。然后其返回一个类,该类用以构造具名元组。
获取字段信息可以通过索引,也可以以属性的形式通过字段名访问。
具名元组独有的属性:
_fields
类属性:一个包含该类所有字段名称的元组;_make(iterable)
类方法:接收可迭代对象生成类的实例,作用相当于City(*iterable)
;_asdict()
实例方法:将具名元组以collections.OrderdDict
的形式返回。
namedtuple
构建的类的实例所消耗的内存与元组一样,因为字段名都被存在对应的类里(类属性)。
切片的基本形式:[a:b[:c]]
,表示在a
和b
之间以c
为间隔取值,其中c
可选,也可为负,若为负则表示反向取值。
而Python 中切片其实是一个slice
切片对象的实例。类似[a:b:c]
返回一个切片对象:slice(a,b,c)
。
[]
里还可以使用逗号分开的索引或切片。要正确地处理这种关系,需要自定义特殊方法__geiitem__
、__setitem__
以元组的形式接收a[i,j]
中的索引。即要得到a[i,j]
的值,Python会调用a.__getitem__(i,j)
。
a[i,j]!=a[i][j]
,即前者需要自定义特殊方法实现,后者是用来处理多维序列的。
多维索引与多维切片的做法主要是为了支持自定义拓展,标准库中并没有相关用法。
切片不止可以用于提取序列内容,对于可变序列,若将切片放在赋值语句左边,或作为del
的操作对象,就可以对序列进行就地修改操作。
示例:
l = list(range(10))
l[2:5] = [20,30]
l
>> [0,1,20,30,5,6,7,8,9]
del l[5:7]
l
>> [0,1,20,30,5,8,9]
如果赋值的对象是一个切片,则赋值语句的右侧必须是个可迭代对象。即便是由单独一个值,也要把它转换成可迭代对象。
对于序列可以使用+
进行拼接、使用*
进行重复,这两个运算符是产生一个新的序列而不修改原有对象。
对
a*n
,若a
含有对其他对象的引用,则需要特别注意,产生的结果中多个元素可能会共享引用。
增量赋值运算符+=
与*=
的表现取决于它们的第一个操作对象。 对于表达式a += b
:
a
实现了__iadd__
(就地加法),则会调用这个方法;__iadd__
,则会调用__add__
,此时表达式的效果等于a = a + b
,首先运算a+b
,再将结果赋值给a
。 即表达式中变量名会不会被关联到新对象取决于该类型有没有实现__iadd__
方法,对应到序列中就是可变序列与不可变序列。即对于内置序列类型而言,使用+=
增量赋值虽然表现出来无差别,但实际上实现的方法不同。
同样以上也适应于*=
,但后者对应的是__imul__
与__mul__
。
对不可变序列进行重复增量拼接的话,效率会很低,因为每次都会产生新的对象,解释器需要将原对象拷贝到新对象,再在新对象上进行追加操作。
一般使用list.sort
方法与内置函数sorted
进行排序。这两个方法都使用Timsort
算法,该算法为稳定的算法。两种的方法的区别为:
list.sort()
方法对列表进行就地排序,返回None
值,只能在列表示例上调用。sorted()
会新建一个已排序列表作为返回值。这个方法可以接收任何形式的可迭代对象作参数,包括不可变对象和生成器;但不管接收何种参数,其始终返回一个列表。而不管是list.sort()
还是sorted()
,都接收两个参数:
reverse
:若为True
,则使用降序排序;默认为False
;key
:一个只接收一个参数的函数,该函数会在序列的每个元素上调用,产生的结果作为排序算法比较的关键字。默认为恒等函数。
* 数组(array):
若我们需要一个只包含数字的列表,那么array.array
将比list
更高效,因为数组背后存的并不是float
对象,而是数字的字节表述。数组支持所有可变序列有关的操作,还提供文件更快的I/O方法。