Python
305 浏览 5 years, 9 months
17.10 列表生成器 (list generator)
版权声明: 转载请注明出处 http://www.codingsoho.com/列表生成器 (list generator)
生成器 generator
不一次性生成所有的列表单元, 一边循环一边计算列表单元, 适合可以推算或有通项的算法, 可以从开始有”有限个”单元推算出后面所有的单元
调用列表生成器返回一个generator对象
格式
(exp for iter_var in iterable if exp1)(formula for 循环)
特点
元素没有生成,所以不能直接使用
- 用for循环来获取所有的元素, 不会出现
StopInteration
错误 - 用
next(list)
函数获取生成的下一个元素(很少用到此方法), 当没有更多的元素时,抛出StopInteration
错误
其它特点同列表生成式
>>> 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 0x0000000000B6A410>
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
>>>
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
g生成的generator,不能直接使用。
>>> g = (x*x for x in range(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
通过next
可以获得下一个元素,超出最后一个后会抛出错误。
自定义generator (用yield语句定义列表生成器)
如果一个函数中含有yield
关键字,它就是一个列表生成器
generator和函数的执行流程不一样
- 函数是顺序执行,遇到
return
或最后一行语句就返回。 - 列表生成器在每次调用
next()
时执行,遇到yield
语句就挂起暂停yield
在这里可以保留fib函数的计算现场,暂停fib的计算并将b返回给调用者- 再次执行时从上次返回的
yield
语句处继续执行 - 如何得到自定义列表生成器的返回值?
send
函数向生成器发送一个值,并触发生成器继续执行,next()
相当于send(None)
- 同样可以用
for
循环来迭代
代码示例
示例:斐波拉切数列
# 函数实现
>>> def fib(max):
... n,a,b = 0,0,1
... while n < max:
... print(b,end="")
... a,b = b,a+b
... n=n+1
... return 'done'
...
>>> fib(6)
112358'done'
函数实现,但是效率非常低
>>> # 列表生成器实现
... def fib_g(max):
... n,a,b = 0,0,1
... while n < max:
... send_value = yield b # 列表生成器语句,返回b值
... a,b = b,a+b
... n = n+1
... return 'done'
...
>>>
>>> g = fib_g(6)
>>> print(next(g),next(g),next(g)) # next触发下一个值
1 1 2
>>> print(g.send(1),g.send(1),g.send(1)) # 使用send触发下一个值
3 5 8
>>> g = fib_g(6)
>>> while True:
... try:
... x = next(g)
... print(x,end=" ")
... except StopIteration as e:
... print('generator return value', e.value)
... break
...
1 1 2 3 5 8 generator return value done
>>> g = fib_g(6)
>>> for x in g:
... print(x, end=" ")
...
1 1 2 3 5 8 >>>
def fib(max): # 函数实现
n,a,b = 0,0,1
while n < max:
print(b,end="")
a,b = b,a+b
n=n+1
return 'done'
# 列表生成器实现
def fib_g(max):
n,a,b = 0,0,1
while n < max:
send_value = yield b
a,b = b,a+b
n = n+1
return 'done'
g = fib_g(6)
print(next(g),next(g),next(g))
print(g.send(1),g.send(1),g.send(1))
g = fib_g(6)
while True:
try:
x = next(g)
print(x,end=" ")
except StopIteration as e:
print('generator return value', e.value)
break
g = fib_g(6)
for x in g:
print(x, end=" ")
多个yield实例
迭代器可以有多个yield输出不同的结果
迭代器停止和输入异常 - 可以通过send异常到迭代器实现一些需要的功能
def dog():
while True:
try:
try:
food = yield # stop once, use next or send(None) to continue
if food is not None:
if food == '':
yield u'Nothing to eat' # stop once
elif food == u'bone':
yield u'delicious' # stop once
elif food == u'vegetable':
yield u'dont like' # stop once
else:
yield u'what is it?' # stop once
else:
yield 'no food' # stop once
except ValueError:
yield u'unhappy' # stop once
except GeneratorExit as e:
print(u'bye bye')
raise StopIteration # need raise exception, otherwise it will not stop
# 此异常需要在外围捕获,本层捕获不了
except StopIteration:
print(u'zzz')
break
d = dog(); next(d) # 启动迭代器,或用send(None) # 停在第一个yield
print(d.send('')) # 从第一个yield继续,停在第二个yield
next(d) # 从第二个yield继续,停在第一个yield
print(d.send('bone')) # 从第一个yield继续,停在第二个yield
next(d) # go on, 从第二个yield继续,停在第一个yield
print(d.send('carrot'));next(d) # 从第一个yield继续,停在第二个yield
print(d.throw(ValueError)) # 从第二个yield继续,停在第一个yield
next(d) # 从第一个yield继续,停在第二个yield
d.close() # 停止迭代器,会产生GeneratorExit异常
next(d) # 生成器已停止,抛出StopIteration异常,并被外围try捕获
执行结果
>>> d = dog(); print(next(d))
None
>>> print(d.send(''))
Nothing to eat
>>> print(next(d))
None
>>> print(d.send('bone'))
delicious
>>> print(next(d))
None
>>> print(d.send('carrot'));print(next(d))
what is it?
None
>>> print(d.throw(ValueError))
unhappy
>>> print(next(d))
None
>>> d.close()
bye bye
zzz
>>> print(next(d))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
杨辉三角练习
定义一个列表生成器triangles(),生成杨辉三角, 期待输出如右所示
# 杨辉三角列表生成器定义
def triangles():
ret = [1]
while len(ret)-1 < 10:
yield ret
ret = [ret[i] + ret[i+1] for i in range(len(ret)-1)]
ret.insert(0,1);
ret.append(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("test pass")
else:
print("test fail")