通过列表生成式可以直接创建一个列表。但是,受到内存限制,列表容量是有限的。如果我们只需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
有没有一种办法,可以不必创建完整的列表,而我们需要访问的元素,可以通过一边循环一边计算的机制得到?答案是肯定的。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
创建generator的方法:
(一)将列表生成式中的 [ ] 改成()
#生成一个列表
L= [x*x for x in range(10)]
#生成一个generator
g = (x*x for x in range(10))
创建列表和生成器的区别仅在于最外层的 [ ] 和()。 [ ] 是创建一个列表,而()是创建一个生成器。
列表可以直接打印出来,而生成器不能直接打印。
print(L)
>>>[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(g)
>>><generator object <genexpr> at 0x0000023E41E93A98>
生成器不能直接打印,如果要一个一个地打印生成器中的元素,可以通过next()函数获得生成器的下一个返回值。
print(next(g))
>>>0
print(next(g))
>>>1
print(next(g))
>>>4
print(next(g))
>>>9
print(next(g))
>>>16
print(next(g))
>>>25
print(next(g))
>>>36
print(next(g))
>>>49
print(next(g))
>>>64
print(next(g))
>>>81
#直到计算到最后一个元素,没有更多元素时,抛出StopIteration的错误。
print(next(g))
>>>Traceback (most recent call last):
File "F:/python_files/ex1.py", line 14, in <module>
print(next(g))
StopIteration
不断调用next()函数似乎过于变态,更好的方法是使用for循环。
for n in g:
print(n)
>>>
0
1
4
9
16
25
36
49
64
81
用for循环来迭代生成器,不需要考虑StopIteration的错误。
(二)生成器函数定义中采用yield关键字
如果一个函数定义中采用了yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器。
#定义一个普通的计算斐波那契数列的函数
def fib1(max):
n,a,b = 0,0,1
while n<max:
print(b)
a,b = b,a+b
n = n+1
return 'done'
fib1(6)
>>>
1
1
2
3
5
8
#定义一个生成器
def fib2(max):
n,a,b = 0,0,1
while n<max:
yield b
a,b = b,a+b
n = n+1
return 'done'
f = fib2(6)
f
>>>无输出,也不报错。
print(f)
>>><generator object fib2 at 0x000001DAD3C83A98>
next(f)
>>>无输出,也不报错。
print(next(f))
>>>1
print(next(f))
>>>1
print(next(f))
>>>2
……
更简单的迭代方法
for n in f:
print(n)
>>>
1
1
2
3
5
8
变成生成器的函数和普通函数的区别:
普通函数是顺序执行,遇到return语句或者最后一行语句就返回。
变成生成器的函数,在每次调用next()函数时执行,遇到yield语句就返回。再次执行时从上次返回的yield语句处继续执行.
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
#生成一个生成器对象(调用该生成器前,首先要生成一个生成器对象)
o = odd()
#用next()函数进行迭代
next(o)
>>>step 1
next(o)
>>>step 2
next(o)
>>>step 3
next(o)
>>>StopIteration
print(next(o))
>>>
step 1
1
print(next(o))
>>>
step 2
3
print(next(o))
>>>
step 3
5
print(next(o))
>>>
StopIteration
但是用for循环调用生成器时,发现拿不到生成器的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
g = fib2(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
练习:
杨辉三角定义如下:
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
把每一行看做一个list,试写一个generator,不断输出下一行的list:
def triangles():
n = 0
L = [1]
while True:
yield L
L = [L[x] + L[x + 1] for x in range(n)]
L.append(1)
L.insert(0, 1)
n = n + 1
n = 0
results = []
for t in triangles():
print(t)
results.append(t)
n = n + 1
if n == 10:
break
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
测试通过!