- 目录
- 列表解析
- 生成器
- 迭代器
列表解析
什么是列表解析?
定义: 基于一个列表生成另外一个列表,或动态生成一个列表。
如:1
2
3
4
5In [26]: [ i ** 2 for i in range(2,8)] # 对原列表乘以2
Out[26]: [4, 9, 16, 25, 36, 49]
In [25]: [ i for i in range(1,10) if i % 2 ] # 对原列表取奇数
Out[25]: [1, 3, 5, 7, 9]
练习1: 有如下文件1
2
3# cat word.txt
render practice inventory indent accelerate,
trigger status,wxq.
我们计算word.txt
单词个数1
2
3
4
5
6
7
8
9
10
11In [29]: f = open('/root/word.txt', 'r')
In [31]: word_list = [ word for line in f for word in line.split() ]
In [36]: print(word_list)
['render', 'practice', 'inventory', 'indent', 'accelerate,', 'trigger', 'status,wxq.']
In [37]: f.seek(0) # 上面读取过文件,指针己然在最后了,这里是把指针移到文件首部
Out[37]: 0
In [38]: len([ word for line in f for word in line.split() ]) # 计算结果为7
Out[38]: 7
我们计算word.txt
非空白字符数1
2
3
4
5In [37]: f.seek(0) # 把指针移到文件首部
Out[37]: 0
In [41]: sum([ len(word) for line in f for word in line.split() ])
Out[41]: 58
练习2: 打印一个3行5列的矩阵1
2
3In [45]: list1 = [ (x+1, y+1) for x in range(3) for y in range(5) ]
In [47]: print(list1)
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
练习3:1
2
3>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
练习4:1
2
3
4In [61]: L = ['I', 'WILL', 'STUDY', 'HARD', 'PYTHON']
In [62]: [ s.capitalize() for s in L ]
Out[62]: ['I', 'Will', 'Study', 'Hard', 'Python']
生成器
列表解析会创建完整列表占用内存,那么有没有一种算法或机制让我们循环使用时推算出后续元素,这样节省了内存空间。
定义:这种边循环边计算的机制,称为生成器generator
创建一个生成器,其中一个方法就是把前面的列表解析[] 换成()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24In [107]: g = (i for i in range(4))
In [108]: g
Out[108]: <generator object <genexpr> at 0x7f98a27ba150>
In [117]: g.send(None)
Out[117]: 0
In [118]: g.send(None)
Out[118]: 1
In [119]: next(g)
Out[119]: 2
In [120]: next(g)
Out[120]: 3
In [122]: next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-122-5f315c5de15b> in <module>()
----> 1 next(g)
StopIteration:
利用yield
创建一个生成器,取出完整元素需要用for 循环来迭代1
2
3
4
5
6
7
8
9
10
11In [123]: def genNum(x):
.....: i = 0
.....: while i < x:
.....: yield i
.....: i += 1
In [124]: g = genNum(5)
In [136]: for i in g:
.....: print(i, end=' ')
.....:
0 1 2 3 4
来看一下yield
工作细节1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17In [138]: def consumer():
.....: print("yield starting")
.....: m = yield 5
.....: print("m_values: %s" % m)
.....: n = yield 12
.....: print("Game over")
.....:
In [139]: c = consumer()
In [140]: c.send(None)
yield starting
Out[140]: 5
In [141]: c.send('to be brave')
m_values: to be brave
Out[141]: 12
小结:
- c.send(None)遇到第一个yield停止,在解释器中out yield中的值,在python yield.py却不会.
- c.send(‘to be brave’)会上次中止处继续,’to be brave’会赋值给yield 5,即m = ‘to be brave’, 遇到第二个yield停止.
- g.send(None)启动迭代器,遇到第一个yield返回.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17root@apt:~/script_study# cat yield.py
#!/usr/bin/env python3
#
def h():
print("yield starting")
m = yield 5
print("m_values: %s" % m)
d = yield 12
print('Game over')
c = h()
# print test info
c.send(None)
c.send('Fighting!')
root@apt:~/script_study# python3 yield.py
yield starting
m_values: Fighting!
后面协程正是用的这个知识点。
迭代器
细心的朋友不难发现生成器可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值为止。
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
- 可以直接作用于for循环的对象统称为可迭代对象:Iterable
哪些是可迭代对象呢?或说可作用于for循环呢?有以下几种数据类型:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
可以使用isinstance()判断一个对象是否是Iterable对象:1
2
3
4
5
6
7
8
9
10
11>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
可以使用isinstance()判断一个对象是否是Iterator对象:1
2
3
4
5
6
7
8
9>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:1
2
3
4>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
为什么list、dict、str等数据类型不是Iterator 呢?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:1
2
3
4
5
6
7
8
9
10# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
总结
1 | 凡是可作用于for循环的对象都是Iterable类型; |