python中的generator, iterator, iterabel

python

先来看看如果遇到一个对象,如何判断其是否是这三种类型:

1 from types import GeneratorType

2 from collectiuons import Iterable, Iterator

3

4 isinstance( xx, GeneratorType )

5 isinstance( xx, Iterable )

6 isinstance( xx, Iterator )

生成器对象:

  生成器是一个通过yield关键字构建的函数,其返回一个generator对象,同时其又是一个iterator对象,因为其实现了__iter__与next方法

In[4]: def gene(k):

... for i in xrange(0, k):

... yield i

...

In[5]: ge = gene(6)

In[6]: type(ge)

Out[6]: generator

In[8]: dir(ge)

Out[8]: [ ...,'__iter__', 'next', ... ]

In[9]: isinstance(ge, collections.Iterator)

Out[9]: True

  生成器的一个特点是只能用一次

In[11]: for i in ge: 

... print i

...

0

1

2

3

4

5

In[12]: ge.next()

Traceback (most recent call last):

File "/usr/lib/python2.7/dist-packages/IPython/core/interactiveshell.py", line 2820, in run_code

exec code_obj in self.user_global_ns, self.user_ns

File "<ipython-input-12-b9172cd6050f>", line 1, in <module>

ge.next()

StopIteration

迭代器对象:

  实现了__iter__与__next__方法的对象。

  __iter__返回一个实例作为最终使用的iterator对象--for...in...语句初始化时使用,next方法是后面每次迭代iterator对象时调用的方法

class Counter(object):

def __init__(self, low, high):

self.current = low

self.high = high

def __iter__(self):

# 'Returns itself as an iterator object'

return self

def next(self):

# 'Returns the next value till current is lower than high'

if self.current > self.high:

raise StopIteration

else:

self.current += 1

return self.current - 1

>>> c = Counter(5,10)

>>> for i in c:

... print(i, end=' ')

...

5 6 7 8 9 10
>>> for i in c:
...  pritn i
...
# 无输出
>>> c.next()

Traceback (most recent call last):

File "<ipython-input-12-b9172cd6050f>"

c.next()

StopIteration

可迭代对象:

  可迭代对象就是用于for...in...循环的

  不同于上面两种,其需要可以多次for...in...使用

  通过generator写可迭代对象:

  将__iter__写成生成器函数。注意for...in...时初始化时调用__iter__函数使得counter为low, 而后执行的都是yield所影响区域的了【1】,除非下次再执行for...in...语句才会调用到counter初始化那句,这样也就实现了复用。但是有个问题是此时Counter的实例就既不是generator也不是iterator对象了。故其也没法调用next方法

class Counter(object):

def __init__(self, low, high):

self.low = low

self.high = high

def __iter__(self):

counter = self.low

while self.high >= counter:

yield counter

counter += 1

obj = Counter(5, 10)

print isinstance(obj, Iterator)

print isinstance(obj, GeneratorType)

print type(obj)

for i in obj:

print i
obj.next()

False
False
<class '__main__.Counter'>
5
6
7
8
9
10

Traceback (most recent call last):
File "/home/pd/..."
obj.next()
AttributeError: 'Counter' object has no attribute 'next'

注释【1】:

In [1]: def gener(k):                                                            

...: print "====initial===="

...: for i in range(0, k):

...: print "before yield"

...: yield i

...: print "after yield"

...: print "*****end******"

...:

In [2]: g = gener(3)

In [3]: for i in g:

...: print g

...:

====initial====

before yield

<generator object gener at 0x7fbc8d15e870>

after yield

before yield

<generator object gener at 0x7fbc8d15e870>

after yield

before yield

<generator object gener at 0x7fbc8d15e870>

after yield

*****end******

补充:

  关于generator的yield与send:

  总的来说:yield返回的是个generator对象,send是向generator输送值

 1 In [35]: def gen():

2 ...: while True:

3 ...: print("before x_yield")

4 ...: x=yield

5 ...: print("after x_yield:", x)

6 ...: yield x*2

7 ...: print("after yield")

8 ...:

9 ...:

10

11 In [36]: g = gen()

12

13 In [37]: g.send(10)

14 ---------------------------------------------------------------------------

15 TypeError Traceback (most recent call last)

16 <ipython-input-37-98b613a2ec82> in <module>()

17 ----> 1 g.send(10)

18

19 TypeError: can't send non-None value to a just-started generator

20

21 In [38]: g.__next__(10)

22 ---------------------------------------------------------------------------

23 TypeError Traceback (most recent call last)

24 <ipython-input-38-bc5eb41f1cb6> in <module>()

25 ----> 1 g.__next__(10)

26

27 TypeError: expected 0 arguments, got 1

28

29 In [39]: g.__next__()

30 before x_yield

31 # 由上可知, “x=yield”中的yield也会成为__next__()调用的终点,也就是说如果上面要两个__next__()才能走完。
31 # 通过实验发现,其实x=yield的执行并不是同时的,比如send(10),此时相当于到达了x=yield中的yield,而此时再执行send(11)才会执行x=yield这个赋值操作
31 # 所以输出是22而不是第一次就能传入的20【见43,44的输出】

32

33 In [40]: g.send(10)

34 after x_yield: 10

35 Out[40]: 20 # 进行到yield x*2

36

37 In [41]: g.__next__()

38 after yield

39 before x_yield

40

41 In [42]: g.send(10)

42 after x_yield: 10

43 Out[42]: 20

44

45 In [43]: g.send(10) #

46 after yield

47 before x_yield

48

49 In [44]: g.send(11)

50 after x_yield: 11

51 Out[44]: 22

补充:关于send与__next__()

 1 In [71]: def gen():

2 ...: print("enhen")

3 ...: while True:

4 ...: print("before yield")

5 ...: yield "pd the handsome"

6 ...: print("afer yield")

7 ...:

8

9 In [72]: g=gen()

10

11 In [73]: g.send(10)

12 ---------------------------------------------------------------------------

13 TypeError Traceback (most recent call last)

14 <ipython-input-73-98b613a2ec82> in <module>()

15 ----> 1 g.send(10)

16

17 TypeError: can't send non-None value to a just-started generator

18

19 In [74]: g.__next__()

20 enhen

21 before yield

22 Out[74]: 'pd the handsome'

23 # 由上可见,send(xx)并不能用于初始化调用generator, 但是通过 send(None)其实可以,纯粹的相当于74的调用__next__()

24 # 可见,send方法实际也调用了__next__()从而执行到了下一次的yield

25 In [75]: g.send(10)

26 afer yield

27 before yield

28 Out[75]: 'pd the handsome'

29

30 In [76]: g.send(20)

31 afer yield

32 before yield

33 Out[76]: 'pd the handsome'

补充:用send实现协程【来源:寥雪峰】

 1 def consumer():

2 r = ''

3 while True:

4 n = yield r

5 if not n:

6 return

7 print('[CONSUMER] Consuming %s...' % n)

8 r = '200 OK'

9

10 def produce(c):

11 c.send(None)

12 n = 0

13 while n < 5:

14 n = n + 1

15 print('[PRODUCER] Producing %s...' % n)

16 r = c.send(n) // 执行一次c

17 print('[PRODUCER] Consumer return: %s' % r)

18 c.close()

19

20 c = consumer()

21 produce(c)

22

23 执行结果:

24

25 [PRODUCER] Producing 1...

26 [CONSUMER] Consuming 1...

27 [PRODUCER] Consumer return: 200 OK

28 [PRODUCER] Producing 2...

29 [CONSUMER] Consuming 2...

30 [PRODUCER] Consumer return: 200 OK

31 [PRODUCER] Producing 3...

32 [CONSUMER] Consuming 3...

33 [PRODUCER] Consumer return: 200 OK

34 [PRODUCER] Producing 4...

35 [CONSUMER] Consuming 4...

36 [PRODUCER] Consumer return: 200 OK

37 [PRODUCER] Producing 5...

38 [CONSUMER] Consuming 5...

39 [PRODUCER] Consumer return: 200 OK

挺叼,传入个generator做操作!

补充:关于yield from :http://simeonvisser.com/posts/python-3-using-yield-from-in-generators-part-1.html

参考:

  http://pymbook.readthedocs.io/en/latest/igd.html

一篇关于yield的好文:

  http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do?rq=1

以上是 python中的generator, iterator, iterabel 的全部内容, 来源链接: utcz.com/z/387532.html

回到顶部