Python学习笔记(五)

python 的一些高级特性(二)

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含 100 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。我们可以通过 next()函数获得 generator 的下一个返回值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
# 列表生成式
>>>L = [x * x for x in range(10)]
>>>L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 生成器
>>>g = (x * x for x in range(10))
>>>g
<generator object <genexpr> at 0x1022ef630>
>>>next(g)
0
>>>next(g)
1
...

通常情况下我们不会直接使用next(),我们会在for循环中使用,所以,我们创建了一个 generator 后,基本上永远不会调用 next(),而是通过 for 循环来迭代它,并且不需要关心 StopIteration 的错误。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现.如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>>g = (x * x for x in range(10))
>>>for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

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
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
# 改为generator形式
def fib_gen(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
# 但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中
g = fib_gen(6)
while True:
try:
x = next(g)
print('g:', x)
except StopIteration as e:
print('Generator return value:', e.value)
break
# g: 1
# g: 1
# g: 2
# g: 3
# g: 5
# g: 8
# Generator return value: done

迭代器

[越努力,越幸运!]