问题:Python的生成器和迭代器之间的区别

迭代器和生成器有什么区别?有关何时使用每种情况的一些示例会有所帮助。

What is the difference between iterators and generators? Some examples for when you would use each case would be helpful.


回答 0

iterator是一个更笼统的概念:其类具有next方法(__next__在Python 3中)和具有__iter__方法的任何对象return self

每个生成器都是一个迭代器,但反之亦然。生成器是通过调用具有一个或多个yield表达式(yield在Python 2.5及更早版本中为语句)的函数而构建的,并且该函数是满足上一段对的定义的对象iterator

当您需要一个具有某种复杂状态维护行为的类,或者想要公开除next(和__iter____init__)之外的其他方法时,您可能想使用自定义迭代器,而不是生成器。通常,一个生成器(有时,对于足够简单的需求,一个生成器表达式)就足够了,并且它更容易编写代码,因为状态维护(在合理范围内)基本上是由挂起和恢复帧“为您完成的”。

例如,一个生成器,例如:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)

或等效的生成器表达式(genexp)

generator = (i*i for i in range(a, b))

将需要更多代码来构建为自定义迭代器:

class Squares(object):
    def __init__(self, start, stop):
       self.start = start
       self.stop = stop
    def __iter__(self): return self
    def next(self): # __next__ in Python 3
       if self.start >= self.stop:
           raise StopIteration
       current = self.start * self.start
       self.start += 1
       return current

iterator = Squares(a, b)

但是,当然,有了类,Squares您可以轻松地提供其他方法,即

    def current(self):
       return self.start

如果您在应用程序中实际需要这种额外功能。

iterator is a more general concept: any object whose class has a next method (__next__ in Python 3) and an __iter__ method that does return self.

Every generator is an iterator, but not vice versa. A generator is built by calling a function that has one or more yield expressions (yield statements, in Python 2.5 and earlier), and is an object that meets the previous paragraph’s definition of an iterator.

You may want to use a custom iterator, rather than a generator, when you need a class with somewhat complex state-maintaining behavior, or want to expose other methods besides next (and __iter__ and __init__). Most often, a generator (sometimes, for sufficiently simple needs, a generator expression) is sufficient, and it’s simpler to code because state maintenance (within reasonable limits) is basically “done for you” by the frame getting suspended and resumed.

For example, a generator such as:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)

or the equivalent generator expression (genexp)

generator = (i*i for i in range(a, b))

would take more code to build as a custom iterator:

class Squares(object):
    def __init__(self, start, stop):
       self.start = start
       self.stop = stop
    def __iter__(self): return self
    def next(self): # __next__ in Python 3
       if self.start >= self.stop:
           raise StopIteration
       current = self.start * self.start
       self.start += 1
       return current

iterator = Squares(a, b)

But, of course, with class Squares you could easily offer extra methods, i.e.

    def current(self):
       return self.start

if you have any actual need for such extra functionality in your application.


回答 1

迭代器和生成器有什么区别?有关何时使用每种情况的一些示例会有所帮助。

总结:迭代器是具有__iter__和方法的对象__next__next在Python 2中)。生成器提供了一种简单的内置方法来创建Iterator的实例。

包含yield的函数仍然是一个函数,在调用该函数时,它会返回生成器对象的实例:

def a_function():
    "when called, returns generator object"
    yield

生成器表达式还返回生成器:

a_generator = (i for i in range(0))

有关更深入的说明和示例,请继续阅读。

生成器迭代器

具体来说,生成器是迭代器的子类型。

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

我们可以通过几种方式创建生成器。一种非常普遍且简单的方法是使用函数。

具体来说,其中包含yield的函数是一个函数,在调用该函数时会返回生成器:

>>> 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'>

同样,生成器是迭代器:

>>> isinstance(a_generator, collections.Iterator)
True

迭代器可迭代的

迭代器是可迭代的

>>> issubclass(collections.Iterator, collections.Iterable)
True

这需要一个__iter__返回Iterator 的方法:

>>> 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__

内置元组,列表,字典,集合,冻结集合,字符串,字节字符串,字节数组,范围和内存视图是可迭代对象的一些示例:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

迭代器需要一个next__next__方法

在Python 2中:

>>> 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中:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__

我们可以使用以下函数从内置对象(或自定义对象)中获取迭代器iter

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

__iter__当您尝试将对象与for循环一起使用时,将调用该方法。然后__next__,在迭代器对象上调用该方法以获取循环中的每个项目。StopIteration耗尽后,迭代器会上升,并且此时无法重用。

从文档中

在“内置类型” 文档的“迭代器类型”部分的“生成器类型”部分中:

Python的生成器提供了一种实现迭代器协议的便捷方法。如果容器对象的__iter__()方法作为生成器实现的,它会自动返回一个迭代器对象(在技术上,一个生成器对象)供给__iter__()next()[ __next__()在Python 3]的方法。有关生成器的更多信息,可以在yield表达式的文档中找到。

(已添加重点。)

因此,我们从中了解到生成器是(便捷的)迭代器类型。

示例迭代器对象

您可以通过创建或扩展自己的对象来创建实现Iterator协议的对象。

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

但是,使用Generator来执行此操作会更容易:

def yes(stop):
    for _ in range(stop):
        yield 'yes'

也许更简单一些,生成器表达式(类似于列表推导):

yes_expr = ('yes' for _ in range(stop))

它们都可以以相同的方式使用:

>>> 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对象扩展为可以迭代的对象时,可以直接使用Iterator协议。

但是,在大多数情况下,最适合yield用于定义返回生成器迭代器或考虑生成器表达式的函数。

最后,请注意,生成器提供了更多的协同程序功能。我将yield在有关“ yield”关键字的作用?”的回答中深入解释Generators和该语句。

What is the difference between iterators and generators? Some examples for when you would use each case would be helpful.

In summary: Iterators are objects that have an __iter__ and a __next__ (next in Python 2) method. Generators provide an easy, built-in way to create instances of Iterators.

A function with yield in it is still a function, that, when called, returns an instance of a generator object:

def a_function():
    "when called, returns generator object"
    yield

A generator expression also returns a generator:

a_generator = (i for i in range(0))

For a more in-depth exposition and examples, keep reading.

A Generator is an Iterator

Specifically, generator is a subtype of iterator.

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

We can create a generator several ways. A very common and simple way to do so is with a function.

Specifically, a function with yield in it is a function, that, when called, returns a generator:

>>> 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'>

And a generator, again, is an Iterator:

>>> isinstance(a_generator, collections.Iterator)
True

An Iterator is an Iterable

An Iterator is an Iterable,

>>> issubclass(collections.Iterator, collections.Iterable)
True

which requires an __iter__ method that returns an Iterator:

>>> 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__

Some examples of iterables are the built-in tuples, lists, dictionaries, sets, frozen sets, strings, byte strings, byte arrays, ranges and memoryviews:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

Iterators require a next or __next__ method

In Python 2:

>>> 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

And in Python 3:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__

We can get the iterators from the built-in objects (or custom objects) with the iter function:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

The __iter__ method is called when you attempt to use an object with a for-loop. Then the __next__ method is called on the iterator object to get each item out for the loop. The iterator raises StopIteration when you have exhausted it, and it cannot be reused at that point.

From the documentation

From the Generator Types section of the Iterator Types section of the Built-in Types documentation:

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__() and next() [__next__() in Python 3] methods. More information about generators can be found in the documentation for the yield expression.

(Emphasis added.)

So from this we learn that Generators are a (convenient) type of Iterator.

Example Iterator Objects

You might create object that implements the Iterator protocol by creating or extending your own object.

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

But it’s easier to simply use a Generator to do this:

def yes(stop):
    for _ in range(stop):
        yield 'yes'

Or perhaps simpler, a Generator Expression (works similarly to list comprehensions):

yes_expr = ('yes' for _ in range(stop))

They can all be used in the same way:

>>> 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

Conclusion

You can use the Iterator protocol directly when you need to extend a Python object as an object that can be iterated over.

However, in the vast majority of cases, you are best suited to use yield to define a function that returns a Generator Iterator or consider Generator Expressions.

Finally, note that generators provide even more functionality as coroutines. I explain Generators, along with the yield statement, in depth on my answer to “What does the “yield” keyword do?”.


回答 2

迭代器:

迭代器是使用next()方法获取序列的下一个值的对象。

生成器:

生成器是一种使用yield方法生成或产生值序列的函数。

生成器函数(如以下示例中的ex:函数)返回的生成next()器对象(如ex f中的示例)的每个方法调用都将按foo()顺序生成下一个值。

调用生成器函数时,它甚至不开始执行函数就返回生成器对象。当next()方法被称为首次,函数开始执行,直到它到达它返回产生值yield语句。收益跟踪(即记住上一次执行)。第二个next()调用从先前的值继续。

下面的示例演示了yield和生成器对象上的next方法的调用之间的相互作用。

>>> 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
>>>

Iterators:

Iterator are objects which uses next() method to get next value of sequence.

Generators:

A generator is a function that produces or yields a sequence of values using yield method.

Every next() method call on generator object(for ex: f as in below example) returned by generator function(for ex: foo() function in below example), generates next value in sequence.

When a generator function is called, it returns an generator object without even beginning execution of the function. When next() method is called for the first time, the function starts executing until it reaches yield statement which returns the yielded value. The yield keeps track of i.e. remembers last execution. And second next() call continues from previous value.

The following example demonstrates the interplay between yield and call to next method on generator object.

>>> 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
>>>

回答 3

添加答案是因为现有答案中没有一个专门解决官方文献中的混乱。

生成器函数是使用yield代替定义的普通函数return。调用时,生成器函数将返回一个生成器对象,它是一种迭代器-它具有一个next()方法。当您调用时next(),将返回生成器函数产生的下一个值。

函数或对象都可以称为“生成器”,具体取决于您阅读的是哪个Python源文档。在Python的词汇说发生器功能,而Python的维基意味着生成器对象。在Python的教程非常设法暗示用三句话的空间用法:

生成器是用于创建迭代器的简单而强大的工具。它们的编写方式与常规函数类似,但是只要要返回数据就使用yield语句。每次在其上调用next()时,生成器都会从上次中断的地方继续(它会记住所有数据值以及最后执行的语句)。

前两个句子用生成器函数标识生成器,而第三句话用生成器对象标识它们。

尽管存在所有这些困惑,但您仍然可以找到Python语言参考来获得清晰明确的词:

yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用yield表达式足以使该定义创建一个生成器函数,而不是普通函数。

调用生成器函数时,它将返回称为生成器的迭代器。然后,该生成器控制生成器功能的执行。

因此,在正式和精确的用法中,“生成器”不合格表示生成器对象,而不是生成器功能。

上面的参考是针对Python 2的,但是Python 3语言参考却说了同样的话。但是,Python 3词汇表指出

generator …通常是指生成器函数,但在某些情况下可能是指生成器迭代器。在预期含义不明确的情况下,使用完整术语可以避免歧义。

Adding an answer because none of the existing answers specifically address the confusion in the official literature.

Generator functions are ordinary functions defined using yield instead of return. When called, a generator function returns a generator object, which is a kind of iterator – it has a next() method. When you call next(), the next value yielded by the generator function is returned.

Either the function or the object may be called the “generator” depending on which Python source document you read. The Python glossary says generator functions, while the Python wiki implies generator objects. The Python tutorial remarkably manages to imply both usages in the space of three sentences:

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).

The first two sentences identify generators with generator functions, while the third sentence identifies them with generator objects.

Despite all this confusion, one can seek out the Python language reference for the clear and final word:

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.

So, in formal and precise usage, “generator” unqualified means generator object, not generator function.

The above references are for Python 2 but Python 3 language reference says the same thing. However, the Python 3 glossary states that

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.


回答 4

每个人都有一个非常好的示例冗长的答案,我对此表示感谢。我只是想为在概念上仍不太清楚的人提供简短的答案:

如果创建自己的迭代器,则涉及到一点点-您必须创建一个类并至少实现iter和next方法。但是,如果您不想经历这种麻烦并想快速创建迭代器,该怎么办。幸运的是,Python提供了一种定义迭代器的捷径。您需要做的就是定义一个至少调用一次yield的函数,现在当您调用该函数时,它将返回“ something ”,其作用类似于迭代器(您可以调用next方法并在for循环中使用它)。这个东西在Python中有一个名字叫做Generator

希望可以澄清一下。

Everybody has a really nice and verbose answer with examples and I really appreciate it. I just wanted to give a short few lines answer for people who are still not quite clear conceptually:

If you create your own iterator, it is a little bit involved – you have to create a class and at least implement the iter and the next methods. But what if you don’t want to go through this hassle and want to quickly create an iterator. Fortunately, Python provides a short-cut way to defining an iterator. All you need to do is define a function with at least 1 call to yield and now when you call that function it will return “something” which will act like an iterator (you can call next method and use it in a for loop). This something has a name in Python called Generator

Hope that clarifies a bit.


回答 5

先前的答案未添加此功能:生成器具有close方法,而典型的迭代器则没有。该close方法StopIteration在生成器中触发一个异常,该异常可能会finally在该迭代器的子句中捕获,从而有机会运行一些清理操作。这种抽象使它比简单的迭代器在大型迭代器中最有用。一个人可以关闭一个生成器,就像一个人可以关闭一个文件一样,而不必担心底层内容。

也就是说,我对第一个问题的个人回答是:iteratable __iter__仅具有一个方法,典型的迭代器__next__仅具有一个方法,生成器具有an __iter__和a __next__以及一个extra close

对于第二个问题,我个人的回答是:在公共界面中,我倾向于偏爱生成器,因为它更具弹性:该close方法具有更大的可组合性yield from。在本地,我可以使用迭代器,但前提是它是一个平面且简单的结构(迭代器不容易编写),并且有理由相信该序列很短,尤其是在序列结束之前可以将其停止的情况。我倾向于将迭代器视为低级原语,而不是文字。

对于控制流而言,生成器是一个与承诺一样重要的概念:两者都是抽象的且可组合的。

Previous answers missed this addition: a generator has a close method, while typical iterators don’t. The close method triggers a StopIteration exception in the generator, which may be caught in a finally clause in that iterator, to get a chance to run some clean‑up. This abstraction makes it most usable in the large than simple iterators. One can close a generator as one could close a file, without having to bother about what’s underneath.

That said, my personal answer to the first question would be: iteratable has an __iter__ method only, typical iterators have a __next__ method only, generators has both an __iter__ and a __next__ and an additional close.

For the second question, my personal answer would be: in a public interface, I tend to favor generators a lot, since it’s more resilient: the close method an a greater composability with yield from. Locally, I may use iterators, but only if it’s a flat and simple structure (iterators does not compose easily) and if there are reasons to believe the sequence is rather short especially if it may be stopped before it reach the end. I tend to look at iterators as a low level primitive, except as literals.

For control flow matters, generators are an as much important concept as promises: both are abstract and composable.


回答 6

生成器功能,生成器对象,生成器:

一个生成器的功能就像Python中的常规功能,但它包含一个或多个yield语句。生成器函数是一个很好的工具,它可以尽可能轻松地创建 Iterator对象。通过generator函数返回的Iterator对象也称为Generator对象Generator

在此示例中,我创建了一个Generator函数,该函数返回Generator对象<generator object fib at 0x01342480>。就像其他迭代器一样,Generator对象可以在for循环中使用,也可以与内置函数一起使用,该 函数next()从generator返回下一个值。

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对象的最简单方法。

迭代器

每个生成器对象都是一个迭代器,但反之则不是。如果自定义迭代器对象的类实现__iter____next__方法(也称为迭代器协议),则可以创建该对象 。

但是,使用生成器函数来创建迭代器要容易得多,因为它们可以简化迭代器的创建,但是自定义迭代器为您提供了更大的自由度,并且您还可以根据需要实现其他方法,如下例所示。

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

Generator Function, Generator Object, Generator:

A Generator function is just like a regular function in Python but it contains one or more yield statements. Generator functions is a great tool to create Iterator objects as easy as possible. The Iterator object returend by generator function is also called Generator object or Generator.

In this example I have created a Generator function which returns a Generator object <generator object fib at 0x01342480>. Just like other iterators, Generator objects can be used in a for loop or with the built-in function next() which returns the next value from generator.

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

So a generator function is the easiest way to create an Iterator object.

Iterator:

Every generator object is an iterator but not vice versa. A custom iterator object can be created if its class implements __iter__ and __next__ method (also called iterator protocol).

However, it is much easier to use generators function to create iterators because they simplify their creation, but a custom Iterator gives you more freedom and you can also implement other methods according to your requirements as shown in the below example.

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

回答 7

强烈推荐Ned Batchelder的示例 用于迭代器和生成器

没有生成器的方法会做一些偶数运算

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them

而使用生成器

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
  • 我们不需要任何清单return声明
  • 对于大/无限长的流有效…它只是走并产生价值

evens照常调用方法(生成器)

num = [...]
for n in evens(num):
   do_smth(n)
  • 生成器也用于打破双回路

迭代器

一整页的书是可迭代的,书签是 迭代器

这个书签除了移动外别无其他 next

litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration  (Exception) as we got end of the iterator

要使用Generator …我们需要一个函数

要使用迭代器……我们需要nextiter

如前所述:

Generator函数返回迭代器对象

迭代器的全部优点:

一次将一个元素存储在内存中

Examples from Ned Batchelder highly recommended for iterators and generators

A method without generators that do something to even numbers

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them

while by using a generator

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
  • We don’t need any list nor a return statement
  • Efficient for large/ infinite length stream … it just walks and yield the value

Calling the evens method (generator) is as usual

num = [...]
for n in evens(num):
   do_smth(n)
  • Generator also used to Break double loop

Iterator

A book full of pages is an iterable, A bookmark is an iterator

and this bookmark has nothing to do except to move next

litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration  (Exception) as we got end of the iterator

To use Generator … we need a function

To use Iterator … we need next and iter

As been said:

A Generator function returns an iterator object

The Whole benefit of Iterator:

Store one element a time in memory


回答 8

您可以将两种方法比较相同的数据:

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)))

此外,如果检查内存占用量,生成器将占用更少的内存,因为它不需要同时将所有值存储在内存中。

You can compare both approaches for the same data:

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)))

Besides, if you check the memory footprint, the generator takes much less memory as it doesn’t need to store all the values in memory at the same time.


回答 9

我用一种非常简单的方式专门为Python新手编写了代码,尽管Python深入人心地做了很多事情。

让我们从最基本的开始:

考虑一个清单,

l = [1,2,3]

让我们编写一个等效的函数:

def f():
    return [1,2,3]

o / p为print(l): [1,2,3]&o / p为print(f()) : [1,2,3]

让列表l变得可迭代:在python中,列表始终是可迭代的,这意味着您可以随时使用迭代器。

让我们在列表上应用迭代器:

iter_l = iter(l) # iterator applied explicitly

让我们迭代一个函数,即编写一个等效的生成器函数。 在python中,一旦您引入了关键字yield;它成为生成器函数,并且迭代器将被隐式应用。

注意:每个生成器始终可以应用隐式迭代器进行迭代,此处隐式迭代器是关键, 因此生成器函数将是:

def f():
  yield 1 
  yield 2
  yield 3

iter_f = f() # which is iter(f) as iterator is already applied implicitly

因此,如果您观察到,一旦创建函数fa generator,它已经是iter(f)

现在,

l是列表,应用迭代器方法“ iter”后,它变为iter(l)

f已经是iter(f),在应用迭代器方法“ iter”之后,它变为iter(iter(f)),再次是iter(f)

您将int强制转换为已经为int的int(x)并保留为int(x)。

例如:

print(type(iter(iter(l))))

<class 'list_iterator'>

永远不要忘记这是Python,而不是C或C ++

因此,以上解释得出的结论是:

列出l〜= iter(l)

生成器函数f == iter(f)

I am writing specifically for Python newbies in a very simple way, though deep down Python does so many things.

Let’s start with the very basic:

Consider a list,

l = [1,2,3]

Let’s write an equivalent function:

def f():
    return [1,2,3]

o/p of print(l): [1,2,3] & o/p of print(f()) : [1,2,3]

Let’s make list l iterable: In python list is always iterable that means you can apply iterator whenever you want.

Let’s apply iterator on list:

iter_l = iter(l) # iterator applied explicitly

Let’s make a function iterable, i.e. write an equivalent generator function. In python as soon as you introduce the keyword yield; it becomes a generator function and iterator will be applied implicitly.

Note: Every generator is always iterable with implicit iterator applied and here implicit iterator is the crux So the generator function will be:

def f():
  yield 1 
  yield 2
  yield 3

iter_f = f() # which is iter(f) as iterator is already applied implicitly

So if you have observed, as soon as you made function f a generator, it is already iter(f)

Now,

l is the list, after applying iterator method “iter” it becomes, iter(l)

f is already iter(f), after applying iterator method “iter” it becomes, iter(iter(f)), which is again iter(f)

It’s kinda you are casting int to int(x) which is already int and it will remain int(x).

For example o/p of :

print(type(iter(iter(l))))

is

<class 'list_iterator'>

Never forget this is Python and not C or C++

Hence the conclusion from above explanation is:

list l ~= iter(l)

generator function f == iter(f)


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。