Python 生成器 Generator 和迭代器 Iterator

#最近一周刚开始接触python,基本的语法,和使用特性和Java差别还是蛮大的。

今天接触到Python的迭代器和生成器有点不是很明白,所以搜索了先关资料整理了一些自己的理解和心得

简述(Profile):

  迭代是Python最强大的功能之一,是访问集合元素的一种方式。

  Iteration one of Python's most powerful functions and a way to access colleaction elements.

  迭代器是一个可以记住遍历位置的对象

  An iterator is an object that can remenber to traverse the location.

  迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退

  The Iterator onject starts from the first element of the collection until all the elements are accessed. The iterator can only move forward without going backwards.

  迭代器有两个基本的方法:iter() 和 next().

  Iterator have two basic methods: iter() and next()

  字符串,列表和元组对象都可用于创建迭代器:

  Strings, lists, and tuples can be used to create iterators:  

>>>list=[1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象
>>> print (next(it))   # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>
实例(Instance)

  迭代器可以使用常规for语句进行遍历:

  Iterators can be traversed using the resular for statements:

#!/usr/bin/python3
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")

  执行以上程序,输出结果如下:

  The above procedures are performed and the output results are as follows:

1 2 3 4

 生成器的背景:(Background Of Generator)

  通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  With a list generation, we can create a list directly. However, with memory constraints, the list capacity is centainly limited. And create a list containing 1 million elements, not only takes up a lof of storage space, if we only need access to the front several elemts, that behind the vast majaority of the elements to take up the space is wasted. 

  所以,如果列表可以通过某种算法算出来,那我们是否可以在循环的过程中不断地推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制称之为生成器(Generator)。

  So, if the list can be calculated by some algorithm, So can we constanly extrapolate the following elements in the loop? This saves a lot of space by not creating a complete list. In Python, the mechanism of this side loop is called a Generator.

  要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

  There are many ways to create a generator. The first method is simple, so if you change a list to (), you create a generator:  

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

  创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

  The difference between creating L and g is only the outermost [] and (), L is a list, and g is a generator.

  如果要一个一个打印出来,可以通过generator的g.__next__()或者next(g)方法:(在Python2 中使用的是g.next()方法)

   If you want to print them one by one, you can use the g.__next() or next(g) mehod of generator:  

>>> g.__next__()
0
>>> g.__next__()
1
>>> g.__next__()
4
>>> g.__next__()
9
>>> g.__next__()
16
>>> g.__next__()
25
>>> g.__next__()
36
>>> g.__next__()
49
>>> g.__next__()
64
>>> g.__next__()
81
>>> g.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

  generator保存的是算法,每次调用__next__(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

  The generator saves the algorithm, and each time it calls __next__(), it computes the value of the next element until the last element is calculated, and when there is no more elements, the error of StopIteration is thrown.

  generator 非常强大。如果推算的算法比较复杂,总类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

  The generator is very powerful. If the calculated algorithm is more complex, the "for loop" of the list generation is not realized, and the function can be used to realize it.

  比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

  For example, the famous Fibonacci sequence (Fibonacci), except for the first and second Numbers, any number can be added by the first two Numbers:

  1, 1, 2, 3, 5, 8, 13, 21, 34, ...

  斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

  The Fibonacci sequence can't be written out of the list, but it's easy to print it out with a function:  

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n = n + 1
                

  上面的函数可以输出斐波那契数列的前N个数:

  The above function can output the first N numbers of the Fibonacci sequence:  

>>> fib(6)
1
1
2
3
5
8

   仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

   If you look closely, you can see  that fib is actually defines the Fibonacci ratched series calculations rules, can start on the first element, determine the subsequent arbitrary elements, this logic is very simialr to the generator.

  也就是说,上面的函数和generator仅一步之遥。要把fib函数的返回值变成generator,只需要把print b改为yield b就可以了:

  In other words, the above functions is just one step away from the generator. To turn the return value of the fib function into the generator, just change the 'print(b) to yield b.

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

  这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,它的返回值就是是一个generator:

  This is another way to define the generator.  If a function definition contains the yield keyword, the function is no longer a normal function, and its return value is a generator.

  举个简单的例子,定义一个generator,依次返回数字1,3,5:

  For a simple example, define a generator and return the number 1,3,5:

>>> def odd():
...     print 'step 1'
...     yield 1
...     print 'step 2'
...     yield 3
...     print 'step 3'
...     yield 5
...
>>> o = odd()
>>> o.__next__()
step 1
1
>>> o.__next__()
step 2
3
>>> o.__next__()
step 3
5
>>> o.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

可以看到,odd不是普通函数,它的返回值是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next()就报错。

As you can see, odd is nor a normal function, and its return value is the generator. In the execution process, the yield is interrunpted and the next time it is executed. After  executing the yield three times, there no yield to be executed, so the forth call to next() is an error.

回到fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

So back to the fib example, we're constanly calling the yield, and it's going to break. Of cause, you have to set a condition for the loop exit the loop, or you'll have a infinite number.

同样的,把函数改成generator后,我们基本上从来不会用next()来调用它,而是直接使用for循环来迭代:

Similarly, after changing the function to the generator, we basically never involke it with next(), but instead use the for loop to iterate:

>>> for n in fib(6):
...     print n
...
1
1
2
3
5
8

Reference Documents: https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00138681965108490cb4c13182e472f8d87830f13be6e88000

posted @ 2017-12-26 16:40  潘彬  阅读(361)  评论(0编辑  收藏  举报