python中的generator, iterator, iterabel

先來看看若是遇到一個對象,如何判斷其是不是這三種類型:html

1 from types import GeneratorType
2 from collectiuons import Iterable, Iterator
3 
4 isinstance( xx, GeneratorType )
5 isinstance( xx, Iterable )
6 isinstance( xx, Iterator )

 

生成器對象:python

  生成器是一個經過yield關鍵字構建的函數,其返回一個generator對象,同時其又是一個iterator對象,由於其實現了__iter__與next方法shell

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

  生成器的一個特色是隻能用一次python2.7

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__方法的對象。post

  __iter__返回一個實例做爲最終使用的iterator對象--for...in...語句初始化時使用,next方法是後面每次迭代iterator對象時調用的方法spa

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

 

可迭代對象:code

  可迭代對象就是用於for...in...循環的協程

  不一樣於上面兩種,其須要能夠屢次for...in...使用htm

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

相關文章
相關標籤/搜索