Python学习笔记(3)-数据类型之列表

(一)列表的概念

序列是Python中最基本的数据结构,和字符串差不多,列表是一系列的数值的序列,序列中的每个元素都分配一个数字,这个数字代表了它的位置,这个数字也叫索引,第一个索引是0,第二个索引是1,依此类推。序列都可以进行的操作包括索引,切片,加,乘,检查成员。

列表是包含若干元素的有序连接内存空间,其形式是中括号。当列表增加或删除元素时,列表对象自动进行内存的扩展或收缩,从而保证元素之间没有缝隙。但是当插入和删除非尾部元素时会涉及列表中大量元素的移动,效率较低,因此除非确实有必要,否则应尽量从列表尾部进行元素的增加与删除。

列表中的元素类型可以不同,可以同时分别为整数、实数、字符串等基本类型,也可以是列表、元组、字典、集合以及其他自定义类型的对象。序列都可以进行的操作包括索引,切片,加,乘,检查成员。

后面还有提到另外一个数据结构元组,元组与列表的区别就在于,无线里面的值不可修改,而列表里面的值可以修改。

列表的创建

常规创建列表

创建一个列表,只要把逗号分隔的不同的数据项使用方括号括起来即可。如下所示:

1
2
3
4
5
6
7
8
9
>>> list1 = ['physics', 'chemistry', 1997, 2000];
>>> list2 = [1, 2, 3, 4, 5 ];
>>> list3 = ["a", "b", "c", "d"];
>>> print(list1)
['physics', 'chemistry', 1997, 2000]
>>> print(list2)
[1, 2, 3, 4, 5]
>>> print(list3)
['a', 'b', 'c', 'd']

使用list函数创建列表

list()函数可以将元组,字符串,字典,集合或其它类型的可迭代对象转换为列表。在将字典转换为列表时,默认是将字典的“键”转换为列表,而不是把字符的元素转换为列表,如果要把字典的元素转换为列表,需要使用字典对象的items()方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> a_list = list((3,5,7,9,11))
>>> # 将元组转换为列表
... print(a_list)
[3, 5, 7, 9, 11]
>>>
>>> b_list = list(range(1,10,2))
>>> print(b_list)
[1, 3, 5, 7, 9]
>>> # 将range对象转换为列表
...
>>> print(list('hello world'))
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> # 将字符串转换为列表
...
>>> print(list({3, 7, 4}))
[3, 4, 7]
>>> # 将集合转换为列表
...
>>> c_list = list({'a':3, 'b':9, 'c':78})
>>> print(c_list)
['a', 'b', 'c']
>>> # 将字典的键转换为列表
...
>>> d_list = list({'a':3,'b':9, 'c':78}.items())
>>> print(d_list)
[('a', 3), ('b', 9), ('c', 78)]
>>> # 将字典的键值转换为列表

列表和字符串

字符串是一系列字符的序列,而列表是一系列值的序列,但一个由字符组成的列表是不同于字符串的。要把一个字符串转换成字符列表,可以用 list这个函数,如下所示:

1
2
3
>>> t = list(s)
>>> t
['s', 'p', 'a', 'm']

判断两个列表是否是同一个对象is

使用的函数是is(),先看一个案例:

1
2
3
4
5
6
>>> a = 'banana'
>>> b = 'banana'
>>> a is b
True
>>> b is a
True

在这个例子中,Python 只建立了一个字符串对象,然后a和b 都指向它。但当你建立两个列表的时候,你得到的就是两个对象了:

1
2
3
4
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

在这个情况下,我们可以说两个列表是相等的,因为它们有相同的元素,但它们不是同一个列表,因为他们并不是同一个对象。如果两个对象是同一个对象,那它们必然是相等的,但如果它们相等,却未必是同一个对象。如果a是一个对象了,然后你赋值c=a,那么这两个变量都指向同一个对象:

1
2
3
4
5
6
7
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
>>> c = a
>>> c is a
True

一个变量和一个对象的关系叫做引用。在上面这个例子中a和c是对同一对象的两个引用。这样一个对象有不止一个引用,就也有了不止一个名字,所以就说这个对象有别名了。如果一个别名对象是可修改的,那么对一个别名做出的修改就会影响到其他引用:

1
2
3
>>> c[0] = 100
>>> a
[100, 2, 3]

这一性质是很有用处的,但很容易让初学者犯错。所以一般来说,处理可变对象的时候,还是尽量避免别名使用,这样更安全些。

删除列表del()

当一个列表不再使用时,可以使用del将其删除,del也可以删除列表,字典等可变序列中的部分元素,而不能删除元组、字符串等不可变序列中的部分元素,如下所示:

1
2
3
4
5
6
7
8
9
10
11
>>> x = [1, 2, 3]
>>> print(x)
[1, 2, 3]
>>> del x[1]
>>> print(x)
[1, 3]
>>> del x
>>> print(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

列表可以删除其中的元素,元组不行。使用del删除对象后,Python会在恰当的时机调用垃圾回收机制来释放内存,在必要的时候也可以导入Python的标准库gc,使用其中的gc.collect()函数立刻启动垃圾回收机制来释放内存。

列表元素的添加append()insert()extend()

append(),insert(),extend()功能:向列表对象中添加元素,其中append()向列表尾部追加一个元素,insert()在列表任意指定位置插入一个元素,extend()用于将另一个列表中的所有元素追加到当前列表中的尾部。这3个方法都属于原地操作,不影响列表对象在内存中的起始地址,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> x = [1, 3, 4]
>>> print(id(x))
2434396012360
>>> # 查看内存地址
... x.append(55)
>>> # 在尾部追加元素
... print(x)
[1, 3, 4, 55]
>>>
>>> x.insert(0,99)
>>> # 在指定位置插入元素
...
>>> x.extend([5, 6, 8])
>>> # 在尾部追加多个元素
...
>>> print(x)
[99, 1, 3, 4, 55, 5, 6, 8]
>>> print(id(x))
2434396012360
>>> # 列表在内存中的地址不变

运算符+*也能实现列表增加元素的目的,但这两个运算符不属于原地操作,而是返回新列表,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> x = [1, 2, 3]
>>> print(id(x))
2434396012296
>>>
>>> x = x + [4]
>>> print(x)
[1, 2, 3, 4]
>>> print(id(x))
2434395797512
>>>
>>> x = x*2
>>> print(x)
[1, 2, 3, 4, 1, 2, 3, 4]
>>> print(id(x))
2434396012296

列表的删除——pop()、remove()、clear()

这3个方法用于删除列表中的元素,其中pop()用于删除并返回指定位置(默认是最后一个)上的元素,remove()用于删除列表中第一个值与指定值相等的元素(当不知道某个值在哪个位置时用比较合适),clear()用于清空列表。这3个方法属于原地操作,不影响列表对象的内存地址。另外,也可以使用del命令删除列表中指定位置的元素,也属于原地操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> x = [1, 2, 3, 4, 5, 6, 7]
>>> print(x.pop())
7
>>> # 弹出并返回尾部元素
...
>>> print(x.pop(0))
1
>>> # 弹出并返回指定位置的元素
...
>>> print(x.clear())
None
>>> # 删除所有元素
...
>>> x2 = [1, 2, 1, 1, 2]
>>> print(x2)
[1, 2, 1, 1, 2]
>>> x2.remove(2)
>>> print(x2)
[1, 1, 1, 2]
>>> # 删除首个值为2的元素
...
>>> del x2[3]
>>> print(x2)
[1, 1, 1]
>>> # 删除指定位置上的元素
...
>>>

注:在列表中间位置插入或删除元素时,会影响该位置后面所有元素后的下标,要尽量避免在列表中间位置进行元素的插入和删除操作。

列表元素统计count()、index()

count()用于返回列表中指定元素中出现的次数,index()用于返回指定元素在列表中首次出现的位置,如果该元素不在列表中则抛出异常。除此之外,成员测试运算符in也可以测试列表中是否存在某个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> x = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4]
>>> x.count(3) #元素3在列表x中出现的次数
3
>>> x.count(5) #元素5在列表x中出现的次数
0
>>> x.index(2) #元素2在列表x中首次出现的索引
1
>>> x.index(4)
6
>>> 5 in x # 5不是列表x的元素
False
>>> 3 in x
True
>>> x.index(5) # 列表中没有5,抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 5 is not in list

列表的排序——sort()和reverse()

列表对象的sort()和reverse()是对列表进行原地排序和逆序,意思是用处理后的数据替换原来的数据,列表中元素原来的顺序丢失。如果原来的顺序不想丢失,可以使用sorted()和reversed()来实现,其中sorted()返回新列表,reversed()返回一个逆序排列后的迭代对象,这两个函数都不对原列表做任何修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> x = list(range(11)) # 生成包含11个整数的列表
>>> print(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> import random
>>> random.shuffle(x)
>>> # shuffle() 方法将序列的所有元素随机排序
... print(x)
[8, 4, 10, 1, 7, 2, 0, 3, 5, 9, 6]
>>>
>>> x.sort(key=lambda item:len(str(item)),reverse = True)
>>> print(x)
[10, 8, 4, 1, 7, 2, 0, 3, 5, 9, 6]
>>>
>>> x.sort(key = str)
>>> print(x)
[0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> x.sort()
>>> print(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

列表的排序——sorted()

sorted()返回新列表,如下所示:

1
2

排序的key参数sort()

关于排序方法的key参数,充分利用列表对象的sort()方法和内置函数sorted()的key参数,可以实现更加复杂的排序,以内置函数sroted()为例说明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
>>> gameresult = [['Bob', 95.0, 'A'],['Alan', 86.0, 'C'],['Mandy', 83.5, 'A'],['Rob', 89.3, 'E']]
>>> from operator import itemgetter
>>> sorted(gameresult, key=itemgetter(2))
[['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E']]
>>> # 按子列表第3个元素进行升序排序
...
>>> sorted(gameresult, key = itemgetter(2, 0))
[['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E']]
>>> # 按第3个元素升序,然后按第1个升序
...
>>> sorted(gameresult, key = itemgetter(2, 0), reverse = True)
[['Rob', 89.3, 'E'], ['Alan', 86.0, 'C'], ['Mandy', 83.5, 'A'], ['Bob', 95.0, 'A']]
>>>
>>> list1 = ["what", "I'm", "sorting", "by"]
>>> # 以一个列表为内容进行排序
...
>>> list2 = ["something", "else", "to", "sort"]
>>> # 对另一个列表的内容进行排序
...
>>> pairs = zip(list1, list2)
>>> # 把两个列表中的对应位置元素配对
...
>>> [item[1] for item in sorted(pairs, key = lambda x:x[0], reverse = True)]
['something', 'to', 'sort', 'else']
>>>
>>> x = [[1, 2, 3], [2, 1, 4], [2, 2, 1]]
>>> # 以第2个元素升序、第3个元素降序排序
... sorted(x, key = lambda item:(item[1], -item[2]))
[[2, 1, 4], [1, 2, 3], [2, 2, 1]]
>>>
>>> x = ['aaaa', 'bc', 'd', 'b', 'ba']
>>> sorted(x, key = lambda item:(len(item), item))
['b', 'd', 'ba', 'bc', 'aaaa']
>>>

列表常用方法的汇总

方法 说明
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的浅复制

内置函数对列表的操作

max(),min()返回列表中所有元素的最大值和最小值;sum()函数用于返回数值型列表中所有元素之和,len()函数用于返回列表中元素的个数,zip()函数用于将多个列表中元素重新组合为元组并返回包含这些元组的zip对象,enumerate()函数返回包含若干下标和值的迭代对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> x = list(range(11))
>>> import random
>>> random.shuffle(x)
>>> print(x)
[3, 2, 5, 10, 6, 1, 7, 4, 9, 0, 8]
>>> max(x)
10
>>> min(x)
0
>>> sum(x)
55
>>> len(x)
11
>>> list(zip(x, [1]*11))
[(3, 1), (2, 1), (5, 1), (10, 1), (6, 1), (1, 1), (7, 1), (4, 1), (9, 1), (0, 1), (8, 1)]
>>> # 多列表重新组合
...
>>> list(zip(range(1,4)))
[(1,), (2,), (3,)]
>>> # zip()函数也可以用于一个序列或迭代对象
...
>>> list(zip(['a', 'b', 'c'],[1,2]))
[('a', 1), ('b', 2)]
>>> # 如果两个列表不等长,以短的为准
...
>>> enumerate(x)
<enumerate object at 0x00000236CD4EBF78>
>>>
>>> list(enumerate(x))
[(0, 3), (1, 2), (2, 5), (3, 10), (4, 6), (5, 1), (6, 7), (7, 4), (8, 9), (9, 0), (10, 8)]
>>>

map()函数

map()函数的用法:

map(func, seq1[, seq2,…])

第一个参数接受一个函数名,后面的参数接受一个或多个可迭代的序列,返回的是一个集合。

Python函数编程中的map()函数是将func作用于seq中的每一个元素,并将所有的调用的结果作为一个list返回。如果func为None,作用同zip()。当seq只有一个时,将函数func作用于这个seq的每个元素上,并得到一个新的seq。

让我们来看一下只有一个seq的时候,map()函数是如何工作的。 一个最简单的例子,如基于f(x) = x^2,map()作用于list [1, 2, 3, 4, 5, 6, 7, 8, 9]后的结果如下:返回结果仍为list。

如下所示:

1
2
3
4
5
6
def power2(x):
return x**2
a = [1,2,3,4,5,6]
print(map(power2,a))
print(list(map(power2,a)))

运行结果如下所示:

1
2
3
4
5
6
7
8
9
>>> def power2(x):
... return x**2
...
>>> a = [1,2,3,4,5,6]
>>>
>>> print(map(power2,a))
<map object at 0x00000236CD4E66A0>
>>> print(list(map(power2,a)))
[1, 4, 9, 16, 25, 36]

map不能直接返回结果,必须要有list作用于结果之上,才能显示为列表。

再看一个map()函数的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
list(map(str, range(5)))
# 将一个函数依次作用到序列对象的每个元素上,这里就是将str这个函数作用于range(5)的每个元素上;
# 转换为字符串
def add5(v):
return v + 5
list(map(add5, range(10)))
def add(x, y):
return x + y
list(map(add, range(5), range(5, 10)))
# 把双参数函数映射到两个序列上
list(map(lambda x, y:x+y, range(5), range(5, 10)))
[add(x,y) for x, y in zip(range(5), range(5, 10))]

运行结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> list(map(str, range(5)))
['0', '1', '2', '3', '4']
>>> # 将一个函数依次作用到序列对象的每个元素上,这里就是将str这个函数作用于range(5)的每个元素上;
... # 转换为字符串
...
>>> def add5(v):
... return v + 5
...
>>> list(map(add5, range(10)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>>
>>> def add(x, y):
... return x + y
...
>>> list(map(add, range(5), range(5, 10)))
[5, 7, 9, 11, 13]
>>> # 把双参数函数映射到两个序列上
...
>>> list(map(lambda x, y:x+y, range(5), range(5, 10)))
[5, 7, 9, 11, 13]
>>> [add(x,y) for x, y in zip(range(5), range(5, 10))]
[5, 7, 9, 11, 13]

zip()函数

zip()函数接受任意多个可迭代对象作为参数(可迭代对象指的是元组列表字典等迭代器。),将对象中对应的元素打包成一个tuple,然后返回一个可迭代的zip对象。这个可迭代对象可以使用循环的方式列出其元素,若多个可迭代对象的长度不一致,则所返回的列表与长度最短的可迭代对象相同。当zip()函数中只有一个参数时,zip(iterable)iterable中依次取一个元组,组成一个元组。在python 3.0中zip()是可迭代对象,使用时必须将其包含在一个list中,方便一次性显示出所有结果,如下所示:

zip()单参数

1
2
3
4
5
list1 = [1, 2, 3, 4]
tuple1 = zip(list1)
list2=list(tuple1)
tuple1
list2

运行结果如下所示:

1
2
3
4
5
6
7
>>> list1 = [1, 2, 3, 4]
>>> tuple1 = zip(list1)
>>> list2=list(tuple1)
>>> tuple1
<zip object at 0x000001F12A4FA608>
>>> list2
[(1,), (2,), (3,), (4,)]

zip()多参数

zip()的参数如果有2个以及2个以上,则以每个以参数中,相同位置的元素组合起来输出,如下所示:

1
2
3
4
5
6
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
xyz = zip(x, y, z)
xyz
list(xyz)

运行结果如下所示:

1
2
3
4
5
6
7
8
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> z = [7, 8, 9]
>>> xyz = zip(x, y, z)
>>> xyz
<zip object at 0x000001F12A4F9E08>
>>> list(xyz)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

从上面的结果可以看出:

  1. zip()函数处理后的结果必须要以list(zip(参数))的形式进行输出;
  2. 如果直接输出zip(参数),结果会显示的是一个对象的内存地址,而非计算结果。

zip(*参数)

使用这个功能可以实现矩阵的行列互换,如下所示:

1
2
3
4
5
6
7
8
9
test=[[1,2,3],
[4,5,6],
[7,8,9]]
test
test_zip = zip(test)
test_star = zip(*test)
list(test_zip)
list(test_star)

运行结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
>>> test=[[1,2,3],
... [4,5,6],
... [7,8,9]]
>>> test
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> test_zip = zip(test)
>>> test_star = zip(*test)
>>>
>>> list(test_zip)
[([1, 2, 3],), ([4, 5, 6],), ([7, 8, 9],)]
>>> list(test_star)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

可以看到,原来只使用zip(参数)则是按照顺序输出结果,如果是zip(*参数)则是按照元素一一对应的关系输出结果,示意图如下所示:

*zip()函数

*zip()函数是zip()的逆函数,先看一个案例:

1
2
3
4
5
6
a = [2, 6, 11]
b = [16, 21, 23]
zipped = zip(a, b)
list(zip(*zip(a, b)))
list(zipped)

运行结果如下所示:

1
2
3
4
5
6
7
8
>>> a = [2, 6, 11]
>>> b = [16, 21, 23]
>>>
>>> zipped = zip(a, b)
>>> list(zip(*zip(a, b)))
[(2, 6, 11), (16, 21, 23)]
>>> list(zipped)
[(2, 16), (6, 21), (11, 23)]

当运行zip(a,b)时,输出的结果为[(2, 16), (6, 21), (11, 23)]

当运行zip(*zip(a,b))时,输出的结果是[(2, 6, 11), (16, 21, 23)]

for循环遍历zip()对象

zip()方法用在for循环中,就会支持并行迭代:

1
2
3
4
5
l1 = [2,3,4]
l2 = [4,5,6]
for (x,y) in zip(l1,l2):
print (x,y,'--',x*y)

运行结果如下所示:

1
2
3
4
5
6
7
8
9
In [3]: l1 = [2,3,4]^M
...: l2 = [4,5,6]^M
...: ^M
...: for (x,y) in zip(l1,l2):^M
...: print (x,y,'--',x*y)
...:
2 4 -- 8
3 5 -- 15
4 6 -- 24

reduce()函数

标准库中functools中的函数reduce()可以将一个接收2个参数的函数以累积的方式从左到右依次作用到一个序列或迭代器对象的所有元素上,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from functools import reduce
>>>
>>> def add(x, y):
... return x + y
...
>>>
>>> seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> reduce(add, range(10))
45
>>>
>>> reduce(lambda x, y:x+y, seq)
45

列表推导式

推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。 共有三种推导,分别为:①列表(list)推导式;②字典(dict)推导式 ;③集合(set)推导式。列表推导式的英文为List Comprehensions,它提供了一种简便的方式创建list,它的语法形式为[表达式 for 变量 in序列或迭代对象]。列表推导式由这些成分构成:①中括号(里面含有表达式);②for(后面跟着其他的for或if语句)。下面是一个案例:

1
2
3
>>> multiples = [i for i in range(30) if i % 3 is 0]
>>> print(multiples)
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

列表推导式案例2:

1
2
3
4
5
def squared(x):
return x*x
multiples = [squared(i) for i in range(30) if i % 3 is 0]
print(multiples)

运行结果如下所示:

1
2
3
4
5
6
>>> def squared(x):
... return x*x
...
>>> multiples = [squared(i) for i in range(30) if i % 3 is 0]
>>> print(multiples)
[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

列表推导式的应用

列表推导式中使用多个循环,实现多序列元素的任意组合,并且可以结合条件语句过滤特定元素,如下所示:

1
2

上面的代码与下面的相同,如下所示:

1
2
3
4
5
6
7
8
9
10
11
biotest@biotest-VirtualBox:~/python3/03file$ cat test4.py
#!/usr/bin/python3
result = []
for x in [1, 2, 3]:
for y in [3,1,4]:
if x != y:
result.append((x,y))
print(result)
biotest@biotest-VirtualBox:~/python3/03file$ python3 test4.py
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

使用列表推导式实现矩阵转置

1
2
3
4
>>> matrix = [[1,2,3,4], [5,6,7,8], [9,10,11,12]]
>>> a = [[row[i] for row in matrix ] for i in range(4)]
>>> print(a)
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

使用内置函数zip()和list()也能实现矩阵转置:

1
2
3
>>> matrix = [[1,2,3,4], [5,6,7,8], [9,10,11,12]]
>>> print(list(map(list,zip(*matrix))))
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

列表推导式的复杂案例

列表推导式中可以使用函数或复杂表达式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
biotest@biotest-VirtualBox:~/python3/03file$ cat test5.py
#!/usr/bin/python3
def f(v):
if v%2 ==0:
v=v**2
else:
v=v+1
return v
print([f(v) for v in [2,3,4,-1] if v > 0])
print([v**2 if v%2 ==0 else v+1 for v in [2, 3, 4,-1] if v > 0])
biotest@biotest-VirtualBox:~/python3/03file$ python3 test5.py
[4, 4, 16]
[4, 4, 16]

元组的概念

元组是Python的一个重要序列结构,从形式上来看,元组的所有元素放在一对圆括号中,元素之间使用逗号分隔,下面来说一下如何创建元组。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x = (1,2,3) # 直接把元组赋值给一个变量
>>> x
(1, 2, 3)
>>> type(x) # 查看变量的类型
<class 'tuple'>
>>> x=(3,) # 如果元组中只有一个元素,则需要在后面加上逗号
>>> x
(3,)
>>> type(x)
<class 'tuple'>
>>> tuple(range(1,5))
(1, 2, 3, 4)
>>>

元组中的元素无法改变,虽然元组支持切片操作,但是只能通过切片来访问元组中的元素,而不支持使用切片来修改元组中元素的值。

但为什么还要使用元组呢?

答:因为Python内部对元组做了大量的优化,访问和处理速度比列表更快。如果定义了一系列常量,主要用途仅是对它们进行遍历或其他类似用途,而不需要对其元素进行任何修改,那么一般建立使用元组而不使用列表。

元组的特殊情况

虽然元组属于不可变序列,其元素值是不可改变的,但是如果元组中包括可变序列,情况又变得复杂一些,例如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x = ([1,2],3) # 元组中包含列表
>>> x
([1, 2], 3)
>>> x[0][0]=5 # 修改元组中列表的元素
>>> x
([5, 2], 3)
>>> x[0].append(8) #为元组中的列表增加元素
>>> x
([5, 2, 8], 3)
>>> x[0]=x[0]+10 # 修改列元组中的值,出错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list

元组的成器推导式

生成器推导式与列表推导式非常类似,只是生成器推导式使用圆括号而不是列表推导式所用的方括号。与列表推导式不同的是,生成器推导式的结果是一个生成器对象,而不是列表,也不是元组。使用生成器对象的元素时,可以根据需要将其转化为列表或元组,也可以使用生成器对象的next()方法或内置函数next()进行遍历,或者直接将其作用迭代器对象来使用。但不管哪种方法访问其元素,当所有元素访问结束后,如果需要重新访问其中的元素,则必须重新创建该生成器对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> g = ((i +2 )**2 for i in range(10)) # 创建生成器对象
>>> g
<generator object <genexpr> at 0x000001898CE64678>
>>> tuple(g) # 将生成器对象转换为元组
(4, 9, 16, 25, 36, 49, 64, 81, 100, 121)
>>> list(g) # 生成器对象已经遍历结束,没有元素了
[]
>>> g = ((i +2 )**2 for i in range(10))
>>> g.__next__() # 使用生成器对象的__next__()方法获取元素
4
>>> g.__next__() # 获取下一个元素
9
>>> next(g) # 使用内置函数next()获取生成器对象中的元素
16
>>> next(g) # 获取下一个元素
25
>>> g = ((i +2 )**2 for i in range(10))
>>> for item in g:
... print(item, end= ' ') # 使用循环直接遍历生成器对象中的元素
...
4 9 16 25 36 49 64 81 100 121 >>>

生成器对象

将列表推导式的[]改成()即可得到生成器,如下所示:

1
2
3
>>> multiples = (i for i in range(30) if i % 3 is 0)
>>> print(type(multiples))
<class 'generator'>

包含yield语句的函数可以用来创建可迭代的生成器对象。下面的代码演示了如何使用生成器来生成斐波那契数列的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def f():
a, b = 1, 1 # 序列解包,同时为多个元素赋值
while True:
yield a # 暂停执行,需要时再产生一个新元素
a, b = b, a+b # 序列解包,继续生成新元素
a = f() # 创建生成器对象
for i in range(10): # 斐波那契数列中前10个元素
print(a.__next__(),end = ' ')
for i in f():
if i > 100: # 斐波那契数列中第一个大于100的元素
print(i, end = ' ')
break
a = f()
print(next(a))
print(next(a))
print(next(a))

运行后,结果如下所示:

1
2
3
4
D:\>python test.py
1 1 2 3 5 8 13 21 34 55 144 1
1
2

参考资料

  1. python3中zip()函数使用详解