轉自 http://lukejin.javaeye.com/blog/587051
前段時間在讀trac 中wiki模塊的源碼的時候,發現了很多地方都使用了yiled這一關鍵詞,
感覺是在需要返回某個值的地方通過yield來代替return,
不是很明白其用法,所以仔細研究下。
一個使用了yiled關鍵字的函數就不再是一個普通的函數了,而是一個生成器函數(generator function),
當函數被調用的時候將返回一個迭代器(iterator)。
所以下面將分別講解迭代器和生成器這兩個概念。
一. 迭代器(Iterator)
寫道
迭代器是一個對象,它實現了迭代器協議,
一般需要實現如下兩個方法
1)next方法
返回容器的下一個元素
2)__iter__方法
返回迭代器自身
對于for語言大家可能都不陌生,我們很多時候需要對一些容器對象進行遍歷就會使用到for循環:
- l=[0,1,2,3,4,5,6]
- for i in l:
- print i
l是一個type為list的對象,這段for-in代碼在運行的時候其實是調用了l的__iter__()函數,返回了一個實現了__next__()或next()(各個版本的python可能不一樣,我試驗的時候所使用的版本為2.6.2)的迭代器對象,每循環一次就會通過next取下一個元素。
當然我們完全沒有必要先把所有的元素都算出來放到一個list里或者其他容器類里進行循環,這樣比較浪費空間。
我們可以直接創建自己的一個迭代器。
-
-
- ''
-
- class Fib:
- ''
-
- def __init__(self, max):
- self.max = max
-
- def __iter__(self):
- self.a = 0
- self.b = 1
- return self
-
- def next(self):
- fib = self.a
- if fib > self.max:
- raise StopIteration
- self.a, self.b = self.b, self.a + self.b
- return fib
定義好了這個Fibonacci迭代器,我們就可以來使用它了。
- from fibonacci2 import Fib
- for n in Fib(1000):
- print n
當調用Fib(1000)的時候,將生成一個迭代器對象,每一次循環都將調用一次next取到下一個值。
所以我們可以看出迭代器有一個很核心的東西就是在循環中,迭代器可以記住之前的狀態。
二.生成器
前面我們說了,任何使用了yield關鍵字的函數都不再是普通的函數了,我們還是來看實例吧,這樣比較容易理解
- def fib(max):
- a, b = 0, 1
- while a < max:
- yield a
- a, b = b, a + b
這里簡單的幾行代碼就實現了上面的迭代器類那么一大堆代碼所實現的功能
使用的時候和上面很類似:
- from fibonacci import fib
- for n in fib(1000):
- print n
引文fib函數使用了yield所以它是一個生成器函數,當我們調用fib(1000)的時候它其實是返回了一個迭代器,且這個迭代器可以控制生成器函數的運行。
我們通過這個返回的迭代器的動作控制fib這個生成器函數的運行。
每當調用一次迭代器的next函數,生成器函數運行到yield之處,返回yield后面的值且在這個地方暫停,所有的狀態都會被保持住,直到下次next函數被調用,或者碰到異常循環退出。
所以生成器的概念還是很簡單的。
三.總結
1.for-in語句在底層都是對一個迭代器對象進行操作的
2.使用了yield關鍵字的函數就是一個生成器函數,被調用的時候生成一個可以控制自己運行的迭代器
實驗:
生成器并不是事先生成所有的結果,而是需要時生成
def fib(max):
a, b = 0, 1
while a < max:
yield a
print 'yield'
a, b = b, a+b
for n in fib(10):
print 'for'
print n
運行結果
for
0
yield
for
1
yield
for
1
yield
for
2
yield
for
3
yield
for
5
yield
for
8
yield