3 序列

序列概述

Python 序列(Sequence)是指按特定顺序依次排列的一组数据,它们可以占用一块连续的内存,也可以分散到多块内存中。 Python中常用的序列结构有列表、元组、字符串,字典、集合以及range、zip、filter等对象也支持很多类似的操作。 列表(list)和元组(tuple)比较相似,它们都按顺序保存元素,所有的元素占用一块连续的内存, 字典(dict)和集合(set)存储的数据都是无序的,每份元素占用不同的内存,

 列表元组字典集合
类型名称listtupledictset
定界符方括号[]圆括号()大括号{}大括号{}
是否可变
是否有序
是否支持下标是(使用序号作为下标)是(使用序号作为下标)是(使用“键”作为下标)
元素分隔符逗号逗号逗号逗号
对元素形式的要求键:值必须可哈希
对元素值的要求“键”必须可哈希必须可哈希
元素是否可重复“键”不允许重复,“值”可以重复
元素查找速度非常慢很慢非常快非常快
新增和删除元素速度尾部操作快 其他位置慢不允许

列表

列表是Python中内置有序、可变序列,列表的所有元素放在一对中括号[]中,并使用逗号分隔开;当列表元素增加或删除时,列表对象自动进行扩展或收缩内存,保证元素之间没有缝隙;在Python中,一个列表中的数据类型可以各不相同。

列表常用方法

方法说明
lst.append(x)将元素x添加至列表lst尾部
lst.extend(L)将列表L中所有元素添加至列表lst尾部
lst.insert(index, x)在列表lst指定位置index处添加元素x,该位置后面的所有元素后移一个位置
lst.remove(x)在列表lst中删除首次出现的指定元素,该元素之后的所有元素前移一个位置
lst.pop([index])删除并返回列表lst中下标为index(默认为-1)的元素
lst.clear()删除列表lst中所有元素,但保留列表对象
lst.index(x)返回列表lst中第一个值为x的元素的下标,若不存在值为x的元素则抛出异常
lst.count(x)返回指定元素x在列表lst中的出现次数
lst.reverse()对列表lst所有元素进行逆序
lst.sort(key=None, reverse=False)对列表lst中的元素进行排序,key用来指定排序依据,reverse决定升序(False)还是降序(True)
lst.copy()返回列表lst的浅复制

列表创建

使用“=”直接将一个列表赋值给变量即可创建列表对象,也可以使用list()函数将元组、range对象、字符串或其他类型的可迭代对象类型的数据转换为列表。

列表删除

当不再使用时,使用del命令删除整个列表。

列表元素的增加

(1)“+”运算符

可以使用“+”运算符将元素添加到列表中。

严格意义上来讲,这并不是真的为列表添加元素,而是创建了一个新列表,并将原列表中的元素和新元素依次复制到新列表的内存空间。由于涉及大量元素的复制,该操作速度较慢,在涉及大量元素添加时不建议使用该方法。

(2)append()方法

使用列表对象的append()方法在当前列表尾部追加元素,原地修改列表,是真正意义上的在列表尾部添加元素,速度较快。

(3)extend()方法

使用列表对象的extend()方法可以将另一个迭代对象的所有元素添加至该列表对象尾部。通过extend()方法来增加列表元素也不改变其内存首地址,属于原地操作

运算符+=类似于列表的extend()方法。

(4)insert()方法

使用列表对象的insert()方法将元素添加至列表的指定位置。

列表的insert()可以在列表的任意位置插入元素,但由于列表的自动内存管理功能,insert()方法会引起插入位置之后所有元素的移动,这会影响处理速度。所以应尽量从列表尾部进行元素的增加与删除操作

类似的还有后面介绍的remove()方法以及使用pop()函数弹出列表非尾部元素和使用del命令删除列表非尾部元素的情况。

(5)“*”运算符

使用乘法来扩展列表对象,将列表与整数相乘,生成一个新列表,新列表是原列表中元素的重复。

当使用*运算符将包含列表的列表重复并创建新列表时,并不是复制子列表值,而是复制已有元素的引用。因此,当修改其中一个值时,相应的引用也会被修改

列表值的修改

列表中包含的是元素值的引用,而不是直接包含元素值。如果是直接修改序列变量的值,则与Python普通变量的情况是一样的。

Python采用的是基于值的自动内存管理方式,当为对象修改值时,并不是真的直接修改变量的值,而是使变量指向新的值,这对于Python所有类型的变量都是一样的。

如果是通过下标来修改序列中元素的值或通过可变序列对象自身提供的方法来增加和删除元素时,序列对象在内存中的起始地址是不变的,仅仅是被改变值的元素地址发生变化,也就是所谓的“原地操作”。

列表元素的删除

(1)del命令

使用del命令删除列表中的指定位置上的元素。

(2)pop()方法

使用列表的pop()方法删除并返回指定位置(默认为最后一个)上的元素,如果给定的索引超出了列表的范围则抛出异常

(3)remove()方法

使用列表对象的remove()方法删除首次出现的指定元素,如果列表中不存在要删除的元素,则抛出异常。

在删除列表元素时,Python会自动对列表内存进行收缩并移动列表元素以保证所有元素之间没有空隙,增加列表元素时也会自动扩展内存并对元素进行移动以保证元素之间没有空隙。每当插入或删除一个元素之后,该元素位置后面所有元素的索引就都改变了。

列表元素访问

使用下标直接访问列表元素,如果指定下标不存在,则抛出异常。

使用列表对象的index()方法获取指定元素首次出现的下标,若列表对象中不存在指定元素,则抛出异常。

列表元素计数

使用列表对象的count()方法统计指定元素在列表对象中出现的次数。

成员资格判断

使用in关键字来判断一个值是否存在于列表中,返回结果为“True”或“False”。

切片操作

切片适用于列表、元组、字符串、range对象等类型,但作用于列表时功能最强大。可以使用切片来截取列表中的任何部分,得到一个新列表,也可以通过切片来修改和删除列表中部分元素,甚至可以通过切片操作为列表对象增加元素。

切片使用2个冒号分隔的3个数字来完成:

  • 第一个数字表示切片开始位置(默认为0)。
  • 第二个数字表示切片截止(但不包含)位置(默认为列表长度)。
  • 第三个数字表示切片的步长(默认为1),当步长省略时可以顺便省略最后一个冒号。

切片操作不会因为下标越界而抛出异常,而是简单地在列表尾部截断或者返回一个空列表,代码具有更强的健壮性。

可以使用del与切片结合来一次性删除多个列表元素。

浅复制与深复制

浅复制

切片返回的是浅复制。所谓浅复制,是指生成一个新的列表,并且把原列表中所选元素的引用都复制到新列表中。如果原列表中只包含整数、实数、复数等基本类型或元组、字符串这样的不可变类型的数据,一般是没有问题的。

如果原列表中包含列表之类的可变数据类型,由于浅复制时只是把子列表的引用复制到新列表中,这样的话修改任何一个都会影响另外一个。

深复制

标准库copy中的deepcopy()函数实现深复制。所谓深复制,是指对原列表中的元素进行递归,把所有的值都复制到新列表中,对嵌套的子列表不再是复制引用。新列表和原列表是互相独立,修改任何一个都不会影响另外一个。

列表排序

使用列表对象的sort()方法进行原地排序,支持多种不同的排序方法。

使用内置函数sorted()对列表进行排序并返回新列表

使用列表对象的reverse()方法将元素原地逆序

使用内置函数reversed()对列表元素进行逆序排列并返回迭代对象

用于序列操作的常用内置函数

all()和any()

all()函数用来测试列表、元组等序列对象以及map对象、zip对象等类似对象中是否所有元素都等价于True,any()函数用来测试序列或可迭代对象中是否存在等价于True的元素。

len()

返回列表中的元素个数,同样适用于元组、字典、集合、字符串等。

max()、 min()

返回列表中的最大或最小元素,同样适用于元组、字典、集合、range对象等。

sum()

对列表的元素进行求和运算,对非数值型列表运算需要指定start参数,同样适用于元组、range。

zip()

返回可迭代的zip对象。

>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b)     # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c)              # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped)          # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]

enumerate()

枚举列表元素,返回枚举对象,其中每个元素为包含下标和值的元组。该函数对元组、字符串同样有效。

>>> for item in enumerate('abcdef'):
    print(item)

(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(5, 'f')

列表推导式

列表推导式使用非常简洁的方式来快速生成满足特定需求的列表,代码具有非常强的可读性。

列表推导式语法形式为:

[expression for expr1 in sequence1 if condition1
       for expr2 in sequence2 if condition2
       for expr3 in sequence3 if condition3
       ...
       for exprN in sequenceN if conditionN]

列表推导式在内部实际上是一个循环结构,只是形式更加简洁。如:

>>> aList = [x*x for x in range(10)]

相当于:

>>> aList = []
>>> for x in range(10):
	    aList.append(x*x)

也相当于:

>>> aList = list(map(lambda x: x*x, range(10)))

例如:

>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]

元组

元组和列表类似,但属于不可变序列,元组一旦创建,用任何方法都不可以修改其元素。 元组的定义方式和列表相同,但定义时所有元素是放在一对圆括号“()”中,而不是方括号中。

元组创建

使用“=”将一个元组赋值给变量。

使用tuple函数将其他序列转换为元组。

元组删除

使用del可以删除元组对象,不能删除元组中的元素。

元组与列表的区别

元组一旦定义就不允许更改。

元组没有append()、extend()和insert()等方法,无法向元组中添加元素。

元组没有remove()或pop()方法,也无法对元组元素进行del操作,不能从元组中删除元素。

元组的速度比列表更快。如果定义了一系列常量值,而所需做的仅是对它进行遍历,那么一般使用元组而不用列表。

元组对不需要改变的数据进行“写保护”将使得代码更加安全。

元组可用作字典的“键”,也可以作为集合的元素。列表不能作为字典的“键”,包含列表、字典、集合或其他类型可变对象的元组也不能做字典的“键”。

如果元组中包含列表或其他类型的可变对象,这些对象是可变的,但元组元素的引用仍是不可变的。

序列解包

可以使用序列解包功能对多个变量同时赋值。

>>> x, y, z = 1, 2, 3             #多个变量同时赋值
>>> v_tuple = (False, 3.5, 'exp')
>>> (x, y, z) = v_tuple
>>> x, y, z = v_tuple
>>> x, y, z = range(3)            #可以对range对象进行序列解包
>>> x, y, z = iter([1, 2, 3])     #使用迭代器对象进行序列解包
>>> x, y, z = map(str, range(3))  #使用可迭代的map对象进行序列解包
>>> a, b = b, a                   #交换两个变量的值
>>> x, y, z = sorted([1, 3, 2])   #sorted()函数返回排序后的列表
>>> a, b, c = 'ABC'               #字符串也支持序列解包
>>> x = [1, 2, 3, 4, 5, 6]
>>> x[:3] = map(str, range(5))    #切片也支持序列解包

序列解包对于列表和字典同样有效

>>> s = {'a':1, 'b':2, 'c':3}
>>> b, c, d = s.items()
>>> b              #Python 3.6之后的版本和Python 3.5之前的版本结果略有不同
('c', 3)
>>> b, c, d = s    #使用字典时不用太多考虑元素的顺序
>>> b
'c'
>>> b, c, d = s.values()
>>> print(b, c, d)
1 3 2

序列解包遍历多个序列

>>> keys = ['a', 'b', 'c', 'd']
>>> values = [1, 2, 3, 4]
>>> for k, v in zip(keys, values):
	  print((k, v), end=' ')

('a', 1) ('b', 2) ('c', 3) ('d', 4) 

使用序列解包遍历enumerate对象

>>> x = ['a', 'b', 'c']
>>> for i, v in enumerate(x):
	  print('The value on position {0} is {1}'.format(i,v))

The value on position 0 is a
The value on position 1 is b
The value on position 2 is c
>>> aList = [1,2,3]
>>> bList = [4,5,6]
>>> cList = [7,8,9]
>>> dList = zip(aList, bList, cList)
>>> for index, value in enumerate(dList):
    print(index, ':', value)

0 : (1, 4, 7)
1 : (2, 5, 8)
2 : (3, 6, 9)

当我们在序列解包时,只想解出部分元素时,可以在变量的左边加“ * ”,该变量就会变成列表,保存多个元素,例:

a = tuple(range(10))
print(a)  # 打印 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

# 只解出0到2,3到9保存为一个列表
b0,b1,b2,*b3 = a
print (b3)  # 打印 [3, 4, 5, 6, 7, 8, 9]

# 只解出8到9,0到7保存为一个列表
*c1,c2,c3 = a
print (c1)  # 打印 [0, 1, 2, 3, 4, 5, 6, 7] 

# 只解出0和9,1到7保存为一个列表
c1,*c2,c3 = a
print(c2)  # 打印 [1, 2, 3, 4, 5, 6, 7, 8]

Python 3.5之后的版本还支持下面用法的序列解包:

>>> print(*[1, 2, 3], 4, *(5, 6))
1 2 3 4 5 6
>>> *range(4),4
(0, 1, 2, 3, 4)
>>> {*range(4), 4, *(5, 6, 7)}
{0, 1, 2, 3, 4, 5, 6, 7}
>>> {'x': 1, **{'y': 2}}
{'y': 2, 'x': 1}

生成器推导式

生成器推导式的结果是一个生成器对象。使用生成器对象的元素时,可以根据需要将其转化为列表或元组,也可以使用生成器对象__next__()方法或内置函数next()进行遍历,或者直接将其作为迭代器对象来使用。

生成器对象具有惰性求值的特点,只在需要时生成新元素,空间占用非常少,尤其适合大数据处理的场合。

不管用哪种方法访问生成器对象,都无法再次访问已访问过的元素

生成器推导式语法形式为:

(expression for expr1 in sequence1 if condition1
       for expr2 in sequence2 if condition2
       for expr3 in sequence3 if condition3
       ...
       for exprN in sequenceN if conditionN)

字典

字典是无序可变序列。

定义字典时,每个元素的键和值用冒号分隔,元素之间用逗号分隔,所有的元素放在一对大括号“{}”中。

字典中的键可以为任意不可变数据,比如整数、实数、复数、字符串、元组等等。

globals()返回包含当前作用域内所有全局变量和值的字典。 locals()返回包含当前作用域内所有局部变量和值的字典。

字典创建

使用=将一个字典赋值给一个变量。

使用dict利用已有数据创建字典。

>>> keys = ['a', 'b', 'c', 'd']
>>> values = [1, 2, 3, 4]
>>> dictionary = dict(zip(keys, values))
>>> dictionary
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> x = dict() #空字典
>>> x
{}

使用dict根据给定的键、值创建字典。

>>> d = dict(name='Dong', age=37)
>>> d
{'name': 'Dong', 'age': 37}

以给定内容为键,创建值为空的字典。

>>> adict = dict.fromkeys(['name', 'age', 'sex'])
>>> adict
{'name': None, 'age': None, 'sex': None}

字典删除

可以使用del删除整个字典。

字典元素的读取

使用下标

以键作为下标可以读取字典元素,若键不存在则抛出异常。

使用字典对象的get()方法

使用字典对象的get()方法获取指定键对应的值,并且可以在键不存在的时候返回指定值。

get()方法语法:

dict.get(key, default=None)
  • key -- 字典中要查找的键。
  • default -- 如果指定键的值不存在时,返回该默认值。

get()方法返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。

>>> print(aDict.get('address'))
None
>>> print(aDict.get('address', 'SDIBT'))
SDIBT

使用字典对象的items()方法

使用字典对象的items()方法可以返回字典的元素。

>>> aDict={'name':'Dong', 'sex':'male', 'age':37}
>>> for item in aDict.items():     #输出字典中所有元素
    print(item)

('age', 37)
('name', 'Dong')
('sex', 'male')
>>> for key in aDict:              #不加特殊说明,默认输出“键”
    print(key)

age
name
sex

使用字典对象的keys()方法

使用字典对象的keys()方法可以返回字典的“键”。

使用字典对象的values()方法

使用字典对象的values()方法可以返回字典的“值”。

>>> for key, value in aDict.items():       #序列解包用法
    print(key, value)

age 37
name Dong
sex male
>>> aDict.keys()                           #返回所有“键”
dict_keys(['name', 'sex', 'age'])
>>> aDict.values()                         #返回所有“值”
dict_values(['Dong', 'male', 37])

字典元素的添加与修改

当以指定键为下标为字典赋值时: 1)若键存在,则可以修改该键的值; 2)若不存在,则表示添加一个键、值对。

使用字典对象的update()方法将另一个字典的键、值对添加到当前字典对象。

使用del删除字典中指定键的元素。

使用字典对象的clear()方法来删除字典中所有元素。

使用字典对象的pop()方法删除并返回指定键的元素。

使用字典对象的popitem()方法删除并返回字典中的一个元素。

字典推导式

字典推导式语法形式为:

{key:value for expr1 in sequence1 if condition1
       for expr2 in sequence2 if condition2
       for expr3 in sequence3 if condition3
       ...
       for exprN in sequenceN if conditionN}

集合

集合是无序可变序列,使用一对大括号界定,元素不可重复,同一个集合中每个元素都是唯一的。

集合中只能包含数字、字符串、元组等不可变类型(或者说可哈希)的数据,而不能包含列表、字典、集合等可变类型的数据。

集合创建

直接将集合赋值给变量。

使用set将其他类型数据转换为集合。

集合删除

当不再使用某个集合时,可以使用del命令删除整个集合。集合对象的pop()方法弹出并删除其中一个元素,remove()方法直接删除指定元素,clear()方法清空集合。

集合运算

交集 & 、并集 | 、差集 - 、对称差集 ^ 等运算

>>> a_set = set([8, 9, 10, 11, 12, 13])
>>> b_set = {0, 1, 2, 3, 7, 8}
>>> a_set | b_set                             #并集
{0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13}
>>> a_set & b_set                             #交集
{8}
>>> a_set - b_set                             #差集
{9, 10, 11, 12, 13}
>>> a_set ^ b_set                             #对称差集
{0, 1, 2, 3, 7, 9, 10, 11, 12, 13}

集合包含关系测试 < >

>>> x = {1, 2, 3}
>>> y = {1, 2, 5}
>>> z = {1, 2, 3, 4}
>>> x < y                                #比较集合大小/包含关系
False
>>> x < z                                #真子集
True
>>> y < z
False
>>> {1, 2, 3} <= {1, 2, 3}               #子集
True

sort() 和 sorted()

列表对象提供了sort()方法支持原地排序,而内置函数sorted()返回新列表,并不对原列表进行任何修改。

sorted()方法可以对列表、元组、字典、range对象等进行排序。

列表的sort()方法和内置函数sorted()都支持key参数实现复杂排序要求。

sorted(items, key=...)