首先我们来看一段 python2 环境下运行的一段代码:

// 注意,在python3.x中range和xrange已被合并,这段代码运行必须在py2下!
for i in range(3):
    print(i)
print('----------')
for i in xrange(3):
    print(i)
print('----------')

lst1 = [x*x for x in range(3)]
lst2 = (x*x for x in range(3))

for i in lst1:
    print(i)
print('----------')
for i in lst2:
    print(i)

打印结果如下:

0
1
2
----------
0
1
2
----------
0
1
4
----------
0
1
4

看了这段代码,你可能会说:python 真麻烦,有 range 又有 xrange,有 [] 又有 (),运行结果还一样。这不是白费事嘛? 
那我们现在稍微改动下代码:

a = range(3)
b = xrange(3)
print(type(a))
print(a)
print(a[0],a[1])
print('---------')
print(type(b))
print(b)
print(b[0],b[1])
print('---------')
lst1 = [x*x for x in range(3)]
lst2 = (x*x for x in range(3))
print(type(lst1))
print(type(lst2))

打印结果如下:

<type 'list'>
[0, 1, 2]
(0, 1)
---------
<type 'xrange'>
xrange(3)
(0, 1)
---------
<type 'list'>
<type 'generator'>

这里需要说明:range() 返回一个列表 list,xrange() 返回一个生成器;也即 [] 返回一个列表 list, 而 () 返回一个生成器。

*注意:

  • 用 [] 推导出来的是迭代器(Iterables)。
  • 用 ()推导出来的是生成器(Generators)。

可迭代对象(iterable) 与 迭代器(iterator)

iterable 可迭代对象是实现了__iter__()方法的对象。更确切的说,是 container.__iter__()方法,该方法返回的是的一个 iterator对象,因此你可以从iterable可迭代对象获得iterator迭代器。

在python中,迭代通常是通过for … in …来完成的,而且只要是可迭代对象(iterable),都能进行迭代。

例如,在下面的遍历中

for value in something

这个 something 就是一个可迭代对象(iterable)。比如 list,string,file。

对于 iterable,我们该关注的是,它是一个能一次返回一个成员的对象(iterable is an object capable of returning its members one at a time),一些 iterable 将所有值都存储在内存中,比如 list,而另一些并不是这样,比如我们下面将讲到的 iterator。

iterator(迭代器)是这样的对象:实现了无参的__next__() 方法,返回序列中的下一个元素;如果没有元素,将会抛出StopIteration异常。Python的迭代器也实现了__iter__()方法,所以一个迭代器(iterator)也是可迭代的(iterable)。

python画图代码大全 python代码生成器_python

iterator.next() 是 iterator 区别于 iterable 的关键了,它允许我们显式地获取一个元素.当调用 next()方法时,实际上产生了2个操作:

  1. 更新 iterator 状态,令其指向后一项,以便下一次调用;
  2. 返回当前结果;

二者的关系如下 :

     iter(可迭代对象) 生成迭代器

1 >>> s = 'abc'       #s是一个可迭代对象
2 >>> it = iter(s)    #it是一个迭代器
3 >>> for i in it:
4 ...     print(i)

我们现在不用 for 循环遍历这个迭代器,我们尝试调用 next() 方法。

1 >>> s = 'abc'
 2 >>> it = iter(s)
 3 >>> next(it)
 4 'a'
 5 >>> s = 'abc'
 6 >>> it = iter(s)
 7 >>> next(it)
 8 'a'
 9 >>> next(it)
10 'b'
11 >>> next(it)
12 'c'
13 >>> next(it)
14 Traceback (most recent call last):
15   File "<stdin>", line 1, in <module>
16 StopIteration
17 >>>

除了最后一行抛出异常外,其他结果和 for循环 一样。也就是说,用 for循环 遍历 迭代器(iterator) X 时,会循环地调用 X 的 next() 方法取得每一次的值,直到 iterator 为空,返回的 StopIteration 作为循环结束的标志。for … in … 会自动处理 StopIteration 异常,从而避免了抛出异常而使程序中断。

我们对一个 可迭代对象(iterable) 用 for … in … 进行迭代时,实际是先通过调用 iter() 方法得到一个 iterator,假设叫做 X, 然后循环地调用 X 的 next() 方法取得每一次的值,直到 iterator 为空,返回的 StopIteration 作为循环结束的标志. for … in … 会自动处理 StopIteration 异常,从而避免了抛出异常而使程序中断.

python画图代码大全 python代码生成器_python画图代码大全_02

需要格外注意的是,iterator 是消耗型的,即每一个值被使用过后,就消失了.因此,你可以将以上的 操作2 理解成 pop.对 iterator 进行遍历之后,其就变成了一个空的容器了,但不等于 None 哦. 因此,若要重复使用 iterator, 利用 list() 方法将其结果保存起来是一个不错的选择。 
代码如下

>>> from collections import Iterable, Iterator
>>> a = [1,2,3]   //# 众所周知,list是一个iterable
>>> b = iter(a)   //# 通过iter()方法,得到iterator,iter()实际上调用了__iter__(),此后不再多说
>>> isinstance(a, Iterable)
True
>>> isinstance(a, Iterator)
False
>>> isinstance(b, Iterable)
True
>>> isinstance(b, Iterator)
True
//# 可见,itertor一定是iterable,但iterable不一定是itertor

//# iterator是消耗型的,用一次少一次.对iterator进行变量,iterator就空了!
>>> c = list(b)
>>> c
[1, 2, 3]
>>> d = list(b)
>>> d
[]


//# 空的iterator并不等于None.
>>> if b:
...   print(1)
...
1
>>> if b == None:
...   print(1)
...

//# 再来感受一下next()
>>> e = iter(a)
>>> next(e)     //#next()实际调用了__next__()方法,此后不再多说
1
>>> next(e)
2

生成器概念

只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。

 

函数返回一个值,而生成器函数生成一个值。不仅如此,函数返回值后就退出了,而生成器可以生产多次值,生成器函数中还能出现多个 yield。

如何“生成”生成器

如何产生一个生成器呢,方法有二:

  1. 生成器函数(generator function):带有yield关键字的函数就是生成器函数,它的返回值是个生成器。 
  2. 生成器表达式(generator expression):而形如(elem for elem in [1, 2, 3])的表达式,称为generator expression,也能产生一个生成器。

生成器表达式生成生成器:

1 >>> gen = ( i for i in [2,3,4,5,6])
 2 >>> gen
 3 <generator object <genexpr> at 0x00000178D648D1A8>
 4 >>> for i in gen:
 5 ...     print(i)
 6 ...
 7 2
 8 3
 9 4
10 5
11 6

我们调用 next() 方法,也是可以的:

1 >>> gen = ( i for i in [2,3,4,5,6])
 2 >>> next(gen)
 3 2
 4 >>> next(gen)
 5 3
 6 >>> next(gen)
 7 4
 8 >>> next(gen)
 9 5
10 >>> next(gen)
11 6
12 >>> next(gen)
13 Traceback (most recent call last):
14   File "<stdin>", line 1, in <module>
15 StopIteration

这和我们上面迭代器(iterator)的使用一毛一样! 
其实,生成器(generator) 就是 迭代器(iterator) 的一种,以更优雅的方式实现的 iterator,而且完全可以像使用 iterator 一样使用 generator。当然除了定义,定义一个 iterator,你需要分别实现 _ _ iter _ _ () 方法和 _ _ next_ _()方法,但 generator 只需要一个 yield 关键字就可以。

生成器函数产生生成器

1 >>> def gen123():
 2 ...     yield 1
 3 ...     yield 2
 4 ...     yield 3
 5 ...
 6 >>> gen123
 7 <function gen123 at 0x00000178D6614D08>
 8 >>> gen123()
 9 <generator object gen123 at 0x00000178D648D1A8>
10 >>> for i in gen123():
11 ...     print(i)
12 ...
13 1
14 2
15 3

这里你也能猜出来,我们调用 next() 方法除了会抛出异常,效果也一样:

1 >>> def gen123():
 2 ...     yield 1
 3 ...     yield 2
 4 ...     yield 3
 5 ...
 6 >>> gen = gen123()
 7 >>> next(gen)
 8 1
 9 >>> next(gen)
10 2
11 >>> next(gen)
12 3
13 >>> next(gen)
14 Traceback (most recent call last):
15   File "<stdin>", line 1, in <module>
16 StopIteration

其实 ,for循环会每次隐时调用next()方法,前进到函数中的下一个yield语句处。

生成器函数执行过程

生成器另外一个黑科技是生成器中的yield语句会挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行。

我们来看代码

1 >>> def genAB():         #❶
 2 ...     print('start')        
 3 ...     yield 'A'        #❷
 4 ...     print('continue')
 5 ...     yield 'B'        #❸
 6 ...     print('end')     #❹
 7 ...
 8 >>> for i in genAB():    #❺
 9 ...     print('--->',i)  #❻
10 ...

打印结果:

1 start
2 ---> A
3 continue
4 ---> B
5 end
  1. 定义生成器函数的方式与普通的函数无异,只不过要使用 yield 关键字。 
  2. 在 for 循环中第一次隐式调用 next() 函数时(序号5),会打印 
  3. ‘start’,然后停在第一个 yield 语句,生成值 ‘A’。 
  4. 第三次调用 next() 函数时,会打印 ‘end.’,然后到达函数定义体的末尾,导致生成器对象抛出 StopIteration 异常。 
  5. 迭代时,for 机制的作用与 g = iter(gen_AB()) 一样,用于获取 
  6. 循环块打印 –> 和 next(g) 返回的值。但是,生成器函数中的 print 函数输出结果之后才会看到这个输出。
  7. ‘start’ 是生成器函数定义体中 print(‘start’) 输出的结果。 
  8. 生成器函数定义体中的 yield ‘A’ 语句会生成值 A,提供给 for 循环使用,而 A 会赋值给变量 i,最终输出 –> A。 
  9. 第二次调用 next(g),继续迭代,生成器函数定义体中的代码由 yield ‘A’ 前进到 yield ‘B’。文本 continue 是由生成器函数定义 体中的第二个 print 函数输出的。
  10. yield ‘B’ 语句生成值 B,提供给 for 循环使用,而 B 会赋值给变 
  11. 第三次调用 next(it),继续迭代,前进到生成器函数的末尾。文本 end. 是由生成器函数定义体中的第三个 print 函数输出的。到达生成 机制会捕获异常,因此循环终止时没有报错。