迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到全部的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,由于人们不多在迭代途中日后退。另外,迭代器的一大优势是不要求事先准备好整个迭代过程当中全部的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这以前或以后,元素能够不存在或者被销毁。这个特色使得它特别适合用于遍历一些巨大的或是无限的集合,好比几个G的文件。python
特色:linux
a)访问者不须要关心迭代器内部的结构,仅需经过next()方法或不断去取下一个内容express
b)不能随机访问集合中的某个值 ,只能从头至尾依次访问编程
c)访问到一半时不能往回退vim
d)便于循环比较大的数据集合,节省内存并发
e)也不能复制一个迭代器。若是要再次(或者同时)迭代同一个对象,只能去建立另外一个迭代器对象。enumerate()的返回值就是一个迭代器,咱们以enumerate为例:app
a = enumerate(['a','b']) for i in range(2): #迭代两次enumerate对象 for x, y in a: print(x,y) print(''.center(50,'-'))
结果:异步
0 a 1 b -------------------------------------------------- --------------------------------------------------
能够看到再次迭代enumerate对象时,没有返回值;ide
咱们能够用linux的文件处理命令vim和cat来理解一下:函数
a) 读取很大的文件时,vim须要好久,cat是毫秒级;由于vim是一次性把文件所有加载到内存中读取;而cat是加载一行显示一行
b) vim读写文件时能够前进,后退,能够跳转到任意一行;而cat只能向下翻页,不能倒退,不能直接跳转到文件的某一页(由于读取的时候这个“某一页“可能尚未加载到内存中)
正式进入python迭代器以前,咱们先要区分两个容易混淆的概念:可迭代对象和迭代器;
能够直接做用于for循环的对象统称为可迭代对象(Iterable)。
能够被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
全部的Iterable都可以经过内置函数iter()来转变为Iterator。
1)可迭代对象
首先,迭代器是一个对象,不是一个函数;是一个什么样的对象呢?就是只要它定义了能够返回一个迭代器的__iter__
方法,或者定义了能够支持下标索引的__getitem__
方法,那么它就是一个可迭代对象。
python中大部分对象都是可迭代的,好比list,tuple等。若是给一个准确的定义的话,看一下list,tuple类的源码,都有__iter__(self)方法。
常见的可迭代对象:
a) 集合数据类型,如list
、tuple
、dict
、set
、str
等;
b) generator
,包括生成器和带yield
的generator function。
注意:生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator,关于生成器,继续往下看
如何判断一个对象是可迭代对象呢?能够经过collections模块的Iterable类型判断:
>>> 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
2)迭代器
一个可迭代对象是不能独立进行迭代的,Python中,迭代是经过for ... in
来完成的。
for循环在迭代一个可迭代对象的过程当中都作了什么呢?
a)当for循环迭代一个可迭代对象时,首先会调用可迭代对象的__iter__()方法,然咱们看看源码中关于list类的__iter__()方法的定义:
def __iter__(self, *args, **kwargs): # real signature unknown """ Implement iter(self). """ pass
__iter__()方法调用了iter(self)函数,咱们再来看一下iter()函数的定义:
def iter(source, sentinel=None): # known special case of iter """ iter(iterable) -> iterator iter(callable, sentinel) -> iterator Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence. In the second form, the callable is called until it returns the sentinel. """ pass
iter()函数的参数是一个可迭代对象,最终返回一个迭代器
b) for循环会不断调用迭代器对象的__next__()方法(python2.x中是next()方法),每次循环,都返回迭代器对象的下一个值,直到遇到StopIteration异常。
>>> lst_iter = iter([1,2,3]) >>> lst_iter.__next__() 1 >>> lst_iter.__next__() 2 >>> lst_iter.__next__() 3 >>> lst_iter.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
这里注意:这里的__next__()方法和内置函数next(iterator, default=None)不是一个东西;(内置函数next(iterator, default=None)也能够返回迭代器的下一个值)
c) 而for循环能够捕获StopIteration异常并结束循环;
总结一下:
a)for....in iterable,会经过调用iter(iterable)函数(实际上,首先调用的对象的__iter__()方法),返回一个迭代器iterator;
b)每次循环,调用一次对象的__next__(self),直到最后一个值,再次调用会触发StopIteration
c)for循环捕捉到StopIteration,从而结束循环
上面说了这么多,到底什么是迭代器Iterator呢?
任何实现了__iter__
和__next__()
(python2中实现next()
)方法的对象都是迭代器,__iter__
返回迭代器自身,__next__
返回容器中的下一个值;
既然知道了什么迭代器,那咱们自定义一个迭代器玩玩:
1 class Iterator_test(object): 2 def __init__(self, data): 3 self.data = data 4 self.index = len(data) 5 6 def __iter__(self): 7 return self 8 9 def __next__(self): 10 if self.index <= 0 : 11 raise StopIteration 12 self.index -= 1 13 return self.data[self.index] 14 15 iterator_winter = Iterator_test('abcde') 16 17 for item in iterator_winter: 18 print(item)
如何判断一个对象是一个迭代器对象呢?两个方法:
1)经过内置函数next(iterator, default=None),能够看到next的第一个参数必须是迭代器;因此迭代器也能够认为是能够被next()函数调用的对象
2)经过collection中的Iterator类型判断
>>> from collections import Iterator >>> >>> isinstance([1,2,3], Iterator) False >>> isinstance(iter([1,2,3]), Iterator) True >>> isinstance([1,2,3].__iter__(), Iterator) True >>>
这里你们会不会有个疑问:
对于迭代器而言,看上去做用的不就是__next__方法嘛,__iter__好像没什么卵用,干吗还须要__iter__方法呢?
咱们知道,python中迭代是经过for循环实现的,而for循环的循环对象必须是一个可迭代对象Iterable,而Iterable必须是一个实现了__iter__方法的对象;知道为何须要__iter__魔术方法了吧;
那么我就是想自定义一个没有实现__iter__方法的迭代器能够吗?能够,像下面这样:
class Iterable_test(object): def __init__(self, data): self.data = data def __iter__(self): return Iterator_test(self.data) class Iterator_test(object): def __init__(self, data): self.data = data self.index = len(data) def __next__(self): if self.index <= 0 : raise StopIteration self.index -= 1 return self.data[self.index] iterator_winter = Iterable_test('abcde') for item in iterator_winter: print(item)
先定义一个可迭代对象(包含__iter__方法),而后该对象返回一个迭代器;这样看上去是否是很麻烦?是否是同时带有__iter__和__next__魔术方法的迭代器更好呢!
同时,这里要纠正以前的一个迭代器概念:只要__next__()
(python2中实现next()
)方法的对象都是迭代器;
既然这样,只须要迭代器Iterator接口就够了,为何还要设计可迭代对象Iterable呢?
这个和迭代器不能重复使用有关,下面赞成讲解:
3)总结和一些重要知识点
a) 如何复制迭代器
以前在使用enumerate时,咱们说过enumerate对象经过for循环迭代一次后就不能再被迭代:
>>> e = enumerate([1,2,3]) >>> >>> for x,y in e: ... print(x,y) ... 0 1 1 2 2 3 >>> for x,y in e: ... print(x,y) ... >>>
这是由于enumerate是一个迭代器;
迭代器是一次性消耗品,当循环之后就空了。不能再次使用;经过深拷贝能够解决;
>>> import copy >>> >>> e = enumerate([1,2,3]) >>> >>> e_deepcopy = copy.deepcopy(e) >>> >>> for x,y in e: ... print(x,y) ... 0 1 1 2 2 3 >>> for x,y in e_deepcopy: ... print(x,y) ... 0 1 1 2 2 3 >>>
b)为何不仅保留Iterator的接口而还须要设计Iterable呢?
由于迭代器迭代一次之后就空了,那么若是list,dict也是一个迭代器,迭代一次就不能再继续被迭代了,这显然是反人类的;因此经过__iter__每次返回一个独立的迭代器,就能够保证不一样的迭代过程不会互相影响。而生成器表达式之类的结果每每是一次性的,不能够重复遍历,因此直接返回一个Iterator就好。让Iterator也实现Iterable的兼容就能够很灵活地选择返回哪种。
总结说,Iterator实现的__iter__是为了兼容Iterable的接口,从而让Iterator成为Iterable的一种实现。
另外,迭代器是惰性的,只有在须要返回下一个数据时它才会计算。就像一个懒加载的工厂,等到有人须要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。因此,Iterator
甚至能够表示一个无限大的数据流,例如全体天然数。而使用list是永远不可能存储全体天然数的。
c)经过__getitem__来实现for循环
前面关于可迭代对象的定义是这样的:定义了能够返回一个迭代器的__iter__
方法,或者定义了能够支持下标索引的__getitem__
方法,那么它就是一个可迭代对象。
可是若是对象没有__iter__,可是实现了__getitem__,会改用下标迭代的方式。
class NoIterable(object): def __init__(self, data): self.data = data def __getitem__(self, item): return self.data[item] no_iter = NoIterable('abcde') for item in no_iter: print(item)
1 class Fib(object): 2 def __init__(self, limit): 3 self.a, self.b = 0, 1 4 self.limit = limit 5 6 def __iter__(self): 7 return self 8 9 def __next__(self): 10 self.a, self.b = self.b, self.a+self.b 11 while self.a > self.limit: 12 raise StopIteration 13 return self.a 14 15 for n in Fib(1000): 16 print(n)
理解了迭代器之后,生成器就会简单不少,由于生成器实际上是一种特殊的迭代器。不过这种迭代器更加优雅。它不须要再像上面的类同样写__iter__()
和__next__()
方法了,只须要一个yiled
关键字。 生成器必定是迭代器(反之不成立),所以任何生成器也是以一种懒加载的模式生成值。
语法上说,生成器函数是一个带yield关键字的函数。
调用生成器函数后会获得一个生成器对象,这个生成器对象实际上就是一个特殊的迭代器,拥有__iter__()
和__next__()
方法
咱们先用一个例子说明一下:
>>> def generator_winter(): ... i = 1 ... while i <= 3: ... yield i ... i += 1 ... >>> generator_winter <function generator_winter at 0x000000000323B9D8> >>> generator_iter = generator_winter() >>> generator_iter <generator object generator_winter at 0x0000000002D9CAF0> >>> >>> generator_iter.__next__() 1 >>> generator_iter.__next__() 2 >>> generator_iter.__next__() 3 >>> generator_iter.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
如今解释一下上面的代码:
a)首先咱们建立了一个含有yield关键字的函数generator_winter,这是一个生成器函数
b)而后,咱们调用了这个生成器函数,而且将返回值赋值给了generator_iter,generator_iter是一个生成器对象;注意generator_iter = generator_winter()时,函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。
c)生成器对象就是一个迭代器,因此咱们能够调用对象的__next__方法来每次返回一个迭代器的值;迭代器的值经过yield返回;而且迭代完最后一个元素后,触发StopIteration异常;
既然生成器对象是一个迭代器,咱们就可使用for循环来迭代这个生成器对象:
>>> def generator_winter(): ... i = 1 ... while i <= 3: ... yield i ... i += 1 ... >>> >>> for item in generator_winter(): ... print(item) ... 1 2 3 >>>
咱们注意到迭代器不是使用return来返回值,而是采用yield返回值;那么这个yield有什么特别之处呢?
1)yield
咱们知道,一个函数只能返回一次,即return之后,此次函数调用就结束了;
可是生成器函数能够暂停执行,而且经过yield返回一个中间值,当生成器对象的__next__()方法再次被调用的时候,生成器函数能够从上一次暂停的地方继续执行,直到触发一个StopIteration
上例中,当执行到yield i后,函数返回i值,而后print这个值,下一次循环,又调用__next__()方法,回到生成器函数,并从yield i的下一句继续执行;
摘一段<python核心编程>的内容:
生成器的另一个方面甚至更增强力----协同程序的概念。协同程序是能够运行的独立函数调用,能够暂停或者挂起,并从程序离开的地方继续或者从新开始。在有调用者和(被调用的)协同程序也有通讯。举例来讲,当协同程序暂停时,咱们仍能够从其中得到一个中间的返回值,当调用回到程序中时,可以传入额外或者改变了的参数,可是仍然可以从咱们上次离开的地方继续,而且全部状态完整。挂起返回出中间值并屡次继续的协同程序被称为生成器,那就是python的生成真正在作的事情。这些提高让生成器更加接近一个彻底的协同程序,由于容许值(和异常)能传回到一个继续的函数中,一样的,当等待一个生成器的时候,生成器如今能返回控制,在调用的生成器能挂起(返回一个结果)以前,调用生成器返回一个结果而不是阻塞的等待那个结果返回。
2) 什么状况会触发StopIteration
两种状况会触发StopIteration
a) 若是没有return,则默认执行到函数完毕时返回StopIteration;
b) 若是在执行过程当中 return,则直接抛出 StopIteration 终止迭代;
c) 若是在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。
>>> def generator_winter(): ... yield 'hello world' ... return 'again' ... >>> >>> winter = generator_winter() >>> winter.__next__() 'hello world' >>> winter.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration: again >>>
3) 生成器的做用
说了这么多,生成器有什么用呢?做为python主要特性之一,这是个极其牛逼的东西,因为它是惰性的,在处理大型数据时,能够节省大量内存空间;
当你须要迭代一个巨大的数据集合,好比建立一个有规律的100万个数字,若是采用列表来存储访问,那么会占用大量的内存空间;并且若是咱们只是访问这个列表的前几个元素,那么后边大部分元素占据的内存空间就白白浪费了;这时,若是采用生成器,则没必要建立完整的列表,一次循环返回一个但愿获得的值,这样就能够大量节省内存空间;
这里在举例以前,咱们先介绍一个生成器表达式(相似于列表推导式,只是把[]换成()),这样就建立了一个生成器。
>>> gen = (x for x in range(10)) >>> gen <generator object <genexpr> at 0x0000000002A923B8> >>>
生成器表达式的语法以下:
(expr for iter_var in iterable if cond_expr)
用生成器来实现斐波那契数列
1 def fib(n): 2 a, b = 0, 1 3 while b <= n: 4 yield b 5 a, b = b, a+b 6 7 f = fib(10) 8 for item in f: 9 print(item)
4)生成器方法
直接看生成器源代码
class __generator(object): '''A mock class representing the generator function type.''' def __init__(self): self.gi_code = None self.gi_frame = None self.gi_running = 0 def __iter__(self): '''Defined to support iteration over container.''' pass def __next__(self): '''Return the next item from the container.''' pass def close(self): '''Raises new GeneratorExit exception inside the generator to terminate the iteration.''' pass def send(self, value): '''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.''' pass def throw(self, type, value=None, traceback=None): '''Used to raise an exception inside the generator.''' pass
首先看到了生成器是自带__iter__和__next__魔术方法的;
a)send
生成器函数最大的特色是能够接受外部传入的一个变量,并根据变量内容计算结果后返回。这是生成器函数最难理解的地方,也是最重要的地方,协程的实现就全靠它了。
看一个小猫吃鱼的例子:
def cat(): print('我是一只hello kitty') while True: food = yield if food == '鱼肉': yield '好开心' else: yield '不开心,人家要吃鱼肉啦'
中间有个赋值语句food = yield,能够经过send方法来传参数给food,试一下:
状况1)
miao = cat() #只是用于返回一个生成器对象,cat函数不会执行 print(''.center(50,'-')) print(miao.send('鱼肉'))
结果:
Traceback (most recent call last): -------------------------------------------------- File "C:/Users//Desktop/Python/cnblogs/subModule.py", line 67, in <module> print(miao.send('鱼肉')) TypeError: can't send non-None value to a just-started generator
看到了两个信息:
a)miao = cat() ,只是用于返回一个生成器对象,cat函数不会执行
b)can't send non-None value to a just-started generator;不能给一个刚建立的生成器对象直接send值
改一下
状况2)
miao = cat() miao.__next__() print(miao.send('鱼肉'))
结果:
我是一只hello kitty
好开心
没毛病,那么到底send()作了什么呢?send()的帮助文档写的很清楚,'''Resumes the generator and "sends" a value that becomes the result of the current yield-expression.''';能够看到send依次作了两件事:
a)回到生成器挂起的位置,继续执行
b)并将send(arg)中的参数赋值给对应的变量,若是没有变量接收值,那么就只是回到生成器挂起的位置
可是,我认为send还作了第三件事:
c)兼顾__next__()做用,挂起程序并返回值,因此咱们在print(miao.send('鱼肉'))时,才会看到'好开心';其实__next__()等价于send(None)
因此当咱们尝试这样作的时候:
1 def cat(): 2 print('我是一只hello kitty') 3 while True: 4 food = yield 5 if food == '鱼肉': 6 yield '好开心' 7 else: 8 yield '不开心,人家要吃鱼肉啦' 9 10 miao = cat() 11 print(miao.__next__()) 12 print(miao.send('鱼肉')) 13 print(miao.send('骨头')) 14 print(miao.send('鸡肉'))
就会获得这个结果:
我是一只hello kitty
None
好开心
None
不开心,人家要吃鱼肉啦
咱们按步骤分析一下:
a)执行到print(miao.__next__()),执行cat()函数,print了”我是一只hello kitty”,而后在food = yield挂起,并返回了None,打印None
b)接着执行print(miao.send('鱼肉')),回到food = yield,并将'鱼肉’赋值给food,生成器函数恢复执行;直到运行到yield '好开心',程序挂起,返回'好开心',并print'好开心'
c)接着执行print(miao.send('骨头')),回到yield '好开心',这时没有变量接收参数'骨头',生成器函数恢复执行;直到food = yield,程序挂起,返回None,并print None
d)接着执行print(miao.send('鸡肉')),回到food = yield,并将'鸡肉’赋值给food,生成器函数恢复执行;直到运行到yield'不开心,人家要吃鱼肉啦',程序挂起,返回'不开心,人家要吃鱼肉啦',,并print '不开心,人家要吃鱼肉啦'
大功告成;那咱们优化一下代码:
1 def cat(): 2 msg = '我是一只hello kitty' 3 while True: 4 food = yield msg 5 if food == '鱼肉': 6 msg = '好开心' 7 else: 8 msg = '不开心,人家要吃鱼啦' 9 10 miao = cat() 11 print(miao.__next__()) 12 print(miao.send('鱼肉')) 13 print(miao.send('鸡肉'))
咱们再看一个更实用的例子,一个计数器
def counter(start_at = 0): count = start_at while True: val = (yield count) if val is not None: count = val else: count += 1 count = counter(5) print(count.__next__()) print(count.__next__()) print(count.send(0)) print(count.__next__()) print(count.__next__())
结果:
5
6
0
1
2
b)close
帮助文档:'''Raises new GeneratorExit exception inside the generator to terminate the iteration.'''
手动关闭生成器函数,后面的调用会直接返回StopIteration异常
>>> def gene(): ... while True: ... yield 'ok' ... >>> g = gene() >>> g.__next__() 'ok' >>> g.__next__() 'ok' >>> g.close() >>> g.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
在close之后再执行__next__会触发StopIteration异常
c)throw
用来向生成器函数送入一个异常,throw()后直接抛出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。
>>> def gene(): ... while True: ... try: ... yield 'normal value' ... except ValueError: ... yield 'we got ValueError here' ... except TypeError: ... break ... >>> g = gene() >>> print(g.__next__()) normal value >>> print(g.__next__()) normal value >>> print(g.throw(ValueError)) we got ValueError here >>> print(g.__next__()) normal value >>> print(g.throw(TypeError)) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
5)经过yield实现单线程状况下的异步并发效果
def consumer(name): print('%s准备吃包子了' % name) while True: baozi_name = yield print('[%s]来了,被[%s]吃了'% (baozi_name, name)) def producer(*name): c1 = consumer(name[0]) c2 = consumer(name[1]) c1.__next__() c2.__next__() for times in range(5): print('作了两个包子') c1.send('豆沙包%s'%times) c2.send('菜包%s'%times) producer('winter', 'elly')
效果:
winter准备吃包子了
elly准备吃包子了
作了两个包子
[豆沙包0]来了,被[winter]吃了
[菜包0]来了,被[elly]吃了
作了两个包子
[豆沙包1]来了,被[winter]吃了
[菜包1]来了,被[elly]吃了
作了两个包子
[豆沙包2]来了,被[winter]吃了
[菜包2]来了,被[elly]吃了
作了两个包子
[豆沙包3]来了,被[winter]吃了
[菜包3]来了,被[elly]吃了
作了两个包子
[豆沙包4]来了,被[winter]吃了
[菜包4]来了,被[elly]吃了
建立了两个独立的生成器,颇有趣,很吊;
6)补充几个小例子:
a)使用生成器建立一个range
def range(n): count = 0 while count < n: yield count count += 1
b ) 使用生成器监听文件输入
def fileTail(filename): with open(filename) as f: while True: tail = f.readline() if line: yield tail else: time.sleep(0.1)
c)计算移动平均值
def averager(start_with = 0): count = 0 aver = start_with total = start_with while True: val = yield aver total += val count += 1 aver = total/count
有个弊端,须要经过__next__或next()初始化一次,经过预激解决
d)预激计算移动平均值
def init(f): def wrapper(start_with = 0): g_aver = f(start_with) g_aver.__next__() return g_aver return wrapper @init def averager(start_with = 0): count = 0 aver = start_with total = start_with while True: val = yield aver total += val count += 1 aver = total/count
e)读取文件字符数最多的行的字符数
最传统的写法:
def longestLine(filename): with open(filename, 'r', encoding='utf-8') as f: alllines = [len(x.strip()) for x in f] return max(alllines)
使用生成器之后的写法:
def longestLine(filename): return max(len(x.strip()) for x in open(filename))
f)多生成器迭代
>>> g = (i for i in range(5)) >>> for j in g: ... print(j) ... 0 1 2 3 4 >>> for j in g: ... print(j) ... >>>
由于for j in g, 每次循环执行一次g.__next__();直到结束,触发StopIteration;
主意下面结果的输出:
>>> g = (i for i in range(4)) >>> g1 = (x for x in g) >>> g2 = (y for y in g1) >>> >>> print(list(g1)) [0, 1, 2, 3] >>> print(list(g2)) [] >>>
为何print(list(g2))为空呢?理一下,否则会乱:
看下面的代码:
1 def g(): 2 print('1.1') 3 for i in range(2): 4 print('1.2') 5 yield i 6 print('1.3') 7 8 def g1(): 9 print('2.1') 10 for x in s: 11 print('2.2') 12 yield x 13 print('2.3') 14 15 def g2(): 16 print('3.1') 17 for y in s1: 18 print('3.2') 19 yield y 20 print('3.3') 21 22 s = g() 23 s1 = g1() 24 s2 = g2() 25 print('start first list') 26 print(list(s1)) 27 print('start second list') 28 print(list(s2))
结果:
1 start first list 2 2.1 3 1.1 4 1.2 5 2.2 6 2.3 7 1.3 8 1.2 9 2.2 10 2.3 11 1.3 12 [0, 1] 13 start second list 14 3.1 15 []
注意第11行以后,g触发了StopIteration,被for x in s捕捉,即不能继续s.__next__()了;一样的g1触发StopIteration,被list捕捉,即不能继续s1.__next__()了;因而打印[0,1]
当进行print(list(s2))时,执行s2.__next__(),停留在代码的第17行for y in s1,可是这是不能继续s1.__next__()了;因而直接触发了StopIteration;结果为[]
再看一个有意思的输出:
def add(n,i): return n+i g = (i for i in range(4)) for n in [1,10]: g = (add(n,i) for i in g) print(list(g))
输出为:
[20, 21, 22, 23]
其实上面的代码翻译以下:
def add(n,i): return n+i def g1(): for i in g: yield add(n,i) def g2(): for i in s1: yield add(n,i) n = 1 s1 = g1() n = 10 s2 = g2() print(list(s2))
最终n用的是10,