Difference between Python's Generators and Iterators
迭代器和生成器之间有什么区别?一些例子说明你什么时候使用每种情况会很有帮助。
每个生成器都是一个迭代器,但不是相反。生成器是通过调用具有一个或多个
当需要具有某种复杂状态维护行为的类时,您可能希望使用自定义迭代器,而不是生成器,或者希望公开除
例如,发电机,例如:
1 2 3 4 5 | def squares(start, stop): for i in range(start, stop): yield i * i generator = squares(a, b) |
或等效的生成器表达式(genexp)
1 | generator = (i*i for i in range(a, b)) |
将需要更多的代码作为自定义迭代器生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Squares(object): def __init__(self, start, stop): self.start = start self.stop = stop def __iter__(self): return self def next(self): if self.start >= self.stop: raise StopIteration current = self.start * self.start self.start += 1 return current iterator = Squares(a, b) |
但是,当然,对于类
1 2 | def current(self): return self.start |
如果您对应用程序中的这些额外功能有任何实际需求。
What is the difference between iterators and generators? Some examples for when you would use each case would be helpful.
总之:迭代器是具有
其中包含yield的函数仍然是一个函数,调用该函数时,返回生成器对象的实例:
1 2 3 | def a_function(): "when called, returns generator object" yield |
生成器表达式还返回生成器:
1 | a_generator = (i for i in range(0)) |
为了更深入地解释和举例,请继续阅读。
生成器是迭代器具体来说,生成器是迭代器的一个子类型。
1 2 3 | >>> import collections, types >>> issubclass(types.GeneratorType, collections.Iterator) True |
我们可以通过多种方式创建一个生成器。一种非常常见和简单的方法是使用函数。
具体地说,其中包含yield的函数是一个函数,当调用它时,它返回一个生成器:
1 2 3 4 5 6 7 8 | >>> def a_function(): "just a function definition with yield in it" yield >>> type(a_function) <class 'function'> >>> a_generator = a_function() # when called >>> type(a_generator) # returns a generator <class 'generator'> |
生成器也是一个迭代器:
1 2 | >>> isinstance(a_generator, collections.Iterator) True |
迭代器是可迭代的
迭代器是不可迭代的,
1 2 | >>> issubclass(collections.Iterator, collections.Iterable) True |
它需要返回迭代器的
1 2 3 4 5 | >>> collections.Iterable() Traceback (most recent call last): File"<pyshell#79>", line 1, in <module> collections.Iterable() TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__ |
iterables的一些示例包括内置的元组、列表、字典、集合、冻结集合、字符串、字节字符串、字节数组、范围和内存视图:
1 2 3 | >>> all(isinstance(element, collections.Iterable) for element in ( (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b''))) True |
迭代器需要
在Python 2中:
1 2 3 4 5 | >>> collections.Iterator() Traceback (most recent call last): File"<pyshell#80>", line 1, in <module> collections.Iterator() TypeError: Can't instantiate abstract class Iterator with abstract methods next |
在python 3中:
1 2 3 4 | >>> collections.Iterator() Traceback (most recent call last): File"<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Iterator with abstract methods __next__ |
我们可以使用
1 2 3 | >>> all(isinstance(iter(element), collections.Iterator) for element in ( (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b''))) True |
当尝试使用带有for循环的对象时,将调用
从内置类型文档的迭代器类型部分的生成器类型部分:
Python’s generators provide a convenient way to implement the iterator protocol. If a container object’s
__iter__() method is implemented as a generator, it will automatically return an iterator object (technically, a generator object) supplying the__iter__() andnext() [__next__() in Python 3] methods. More information about generators can be found in the documentation for the yield expression.
(增加了重点。)
因此,我们从中了解到生成器是一种(方便的)迭代器类型。
迭代器对象示例您可以通过创建或扩展自己的对象来创建实现迭代器协议的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Yes(collections.Iterator): def __init__(self, stop): self.x = 0 self.stop = stop def __iter__(self): return self def next(self): if self.x < self.stop: self.x += 1 return 'yes' else: # Iterators must raise when done, else considered broken raise StopIteration __next__ = next # Python 3 compatibility |
但简单地使用发电机来实现这一点比较容易:
1 2 3 | def yes(stop): for _ in range(stop): yield 'yes' |
或者更简单,一个生成器表达式(类似于列表理解):
1 | yes_expr = ('yes' for _ in range(stop)) |
它们都可以以相同的方式使用:
1 2 3 4 5 6 7 8 9 | >>> stop = 4 >>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), ('yes' for _ in range(stop))): ... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3)) ... 0: yes == yes == yes 1: yes == yes == yes 2: yes == yes == yes 3: yes == yes == yes |
结论
当需要将Python对象扩展为可以迭代的对象时,可以直接使用迭代器协议。
然而,在绝大多数情况下,您最适合使用
最后,注意生成器作为协程提供了更多的功能。我解释了生成器,以及
Iterators:
迭代器是使用
发电机:
生成器是使用
Generator函数(对于下面的示例中的示例:
当调用一个生成器函数时,它返回一个生成器对象,而不必开始执行该函数。当第一次调用
下面的示例演示yield和对generator对象的next方法调用之间的相互作用。
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 | >>> def foo(): ... print"begin" ... for i in range(3): ... print"before yield", i ... yield i ... print"after yield", i ... print"end" ... >>> f = foo() >>> f.next() begin before yield 0 # Control is in for loop 0 >>> f.next() after yield 0 before yield 1 # Continue for loop 1 >>> f.next() after yield 1 before yield 2 2 >>> f.next() after yield 2 end Traceback (most recent call last): File"<stdin>", line 1, in <module> StopIteration >>> |
增加一个答案,因为现有的答案中没有一个专门解决官方文献中的混乱。
生成器函数是使用
函数或对象可以被称为"生成器",这取决于您读取的是哪个Python源文档。python词汇表表示生成器函数,而python wiki则表示生成器对象。python教程在三个句子的空格中显著地暗示了这两种用法:
Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called on it, the generator resumes where it left off (it remembers all the data values and which statement was last executed).
前两个句子用生成器函数标识生成器,而第三个句子用生成器对象标识生成器。
尽管如此,我们还是可以找到Python语言的参考,以获得清晰和最终的单词:
The yield expression is only used when defining a generator function, and can only be used in the body of a function definition. Using a yield expression in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.
When a generator function is called, it returns an iterator known as a generator. That generator then controls the execution of a generator function.
所以,在正式而精确的使用中,"发电机"不合格是指发电机对象,而不是发电机功能。
上面的引用是针对python 2的,但是python 3语言的引用也说了同样的话。但是,python 3术语表指出
generator ... Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.
每个人都有一个非常好和详细的例子回答,我非常感谢。我只是想给那些概念上还不太清楚的人一个简短的回答:
如果您创建自己的迭代器,它有点复杂——您已经创建一个类,至少实现ITER和下一个方法。但如果您不想经历这些麻烦,并且希望快速创建迭代器,该怎么办呢?幸运的是,Python提供了一种定义迭代器的快捷方式。您所需要做的就是定义一个函数,其中至少有一个要生成的调用,现在当您调用该函数时,它将返回"something",其作用类似于迭代器(您可以调用下一个方法并在for循环中使用它)。这个东西在python中有个名字叫做generator
希望能澄清一点。
Generator Function, Generator Object, Generator:
生成器函数与Python中的常规函数一样,但它包含一个或多个
在这个例子中,我创建了一个生成器函数,它返回一个生成器对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def fib(max): a, b = 0, 1 for i in range(max): yield a a, b = b, a + b print(fib(10)) #<generator object fib at 0x01342480> for i in fib(10): print(i) # 0 1 1 2 3 5 8 13 21 34 print(next(myfib)) #0 print(next(myfib)) #1 print(next(myfib)) #1 print(next(myfib)) #2 |
因此,生成器函数是创建迭代器对象的最简单方法。
Iterator:
每个生成器对象都是一个迭代器,但不是相反。如果自定义迭代器对象的类实现了
但是,使用generator函数创建迭代器要容易得多,因为它们简化了迭代器的创建,但是自定义迭代器给您更多的自由,您还可以根据您的需求实现其他方法,如下面的示例所示。
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 | class Fib: def __init__(self,max): self.current=0 self.next=1 self.max=max self.count=0 def __iter__(self): return self def __next__(self): if self.count>self.max: raise StopIteration else: self.current,self.next=self.next,(self.current+self.next) self.count+=1 return self.next-self.current def __str__(self): return"Generator object" itobj=Fib(4) print(itobj) #Generator object for i in Fib(4): print(i) #0 1 1 2 print(next(itobj)) #0 print(next(itobj)) #1 print(next(itobj)) #1 |
以前的回答没有注意到这一点:一个生成器有一个
也就是说,我个人对第一个问题的回答是:可迭代只有一个
对于第二个问题,我个人的回答是:在公共接口中,我倾向于使用生成器,因为它更具弹性:
对于控制流问题,生成器和承诺一样重要:两者都是抽象的和可组合的。
对于相同的数据,可以比较两种方法:
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 | def myGeneratorList(n): for i in range(n): yield i def myIterableList(n): ll = n*[None] for i in range(n): ll[i] = i return ll # Same values ll1 = myGeneratorList(10) ll2 = myIterableList(10) for i1, i2 in zip(ll1, ll2): print("{} {}".format(i1, i2)) # Generator can only be read once ll1 = myGeneratorList(10) ll2 = myIterableList(10) print("{} {}".format(len(list(ll1)), len(ll2))) print("{} {}".format(len(list(ll1)), len(ll2))) # Generator can be read several times if converted into iterable ll1 = list(myGeneratorList(10)) ll2 = myIterableList(10) print("{} {}".format(len(list(ll1)), len(ll2))) print("{} {}".format(len(list(ll1)), len(ll2))) |
此外,如果检查内存足迹,生成器占用的内存要少得多,因为它不需要同时将所有值存储在内存中。