迭代器
迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的對象境析,迭代器對象從集合的第一個元素開始訪問义矛,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會后退缸榛。
可迭代對象
list吝羞,tuple,str内颗,dict等類型的數(shù)據(jù)可以使用for ... in ... 的循環(huán)語法從其中依次拿到數(shù)據(jù)進(jìn)行使用钧排,我們把這樣的過程稱為遍歷,也加迭代均澳。
自己實現(xiàn)一個可以迭代的對象
# coding=utf-8
import time
from collections import Iterable
from collections import Iterator
class Classmate(object):
def __init__(self):
self.names = list()
self.current_num = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
''' 如果想要一個對象可以迭代恨溜,那么必須實現(xiàn)__iter__方法 '''
return self
def __next__(self):
if self.current_num < len(self.names):
ret = self.names[self.current_num]
self.current_num += 1
return ret
else:
raise StopIteration
classmate = Classmate()
classmate.add("老王")
classmate.add("王二")
classmate.add("張三")
for name in classmate:
print name
實現(xiàn)斐波拉契數(shù)列
# coding=utf-8
class FibIterator(object):
'''斐波拉契數(shù)列迭代器'''
def __init__(self, n):
'''
:param n: int, 指明生成數(shù)列的前n個數(shù)
'''
self.n = n
# current 保存當(dāng)前生成到數(shù)列中的第幾個數(shù)
self.current = 0
# num1 保存前前一個數(shù),初始值為數(shù)列中的第一個數(shù) 0
self.num1 = 0
# num2 保存前一個數(shù)找前,初始值是數(shù)列中的第二個數(shù) 1
self.num2 = 1
def __next__(self):
''' 被next()函數(shù)調(diào)用來獲取下一個數(shù) '''
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, sefl.num1+self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
''' 迭代器的__iter__返回自身即可 ’‘’
return self
if __name__ == "__main__":
fib = FibIterator(10)
for num in fib:
print num
生成器
利用迭代器糟袁,我們可以在每次迭代獲取數(shù)據(jù)(通過next()方法)時按照特定的規(guī)律進(jìn)行生成。但是我們在實現(xiàn)一個迭代器時躺盛,關(guān)于當(dāng)前迭代到的狀態(tài)需要自己記錄项戴,進(jìn)而才能根據(jù)當(dāng)前狀態(tài)生成下一個數(shù)據(jù)。為了達(dá)到記錄當(dāng)前狀態(tài)槽惫,并配合next()函數(shù)進(jìn)行迭代使用周叮,我們可以采用更加簡便的方法---生成器辩撑。
生成器是一種特殊的迭代器
創(chuàng)建生成器方法1
要創(chuàng)建一個生成器,有很多方法仿耽。第一種方法合冀,只要把一個列表生成式的[]改為()
L = [x*2 for x in range(5)]
print L
>>> [0, 2, 4, 6, 8]
G = (x*2 for x in range(5))
print G
>>> <generator object <genexpr> at 0x7f626c123db0>
# 創(chuàng)建L和G的區(qū)別僅在于最外層的[]和(),L是一個列表项贺,而G是一個生成器君躺。
# 我們可以直接打印列表L中每一個元素,而對于生成器G敬扛。我們可以按照迭代器的使用方式來使用晰洒。
# 即可以通過next()函數(shù),for循環(huán)啥箭,list()等方法來使用谍珊。
next(G)
>>> 0
next(G)
>>> 2
next(G)
>>> 4
創(chuàng)建生成器方法2
generator 非常強(qiáng)大。如果推算的算法比較復(fù)雜急侥,用類似列表生成式的for循環(huán)無法實現(xiàn)的時候砌滞,還可以使用函數(shù)來實現(xiàn)。
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
# print a
yield a # 如果一個函數(shù)中有yield語句坏怪,那么就不函數(shù)贝润,而是一個生成器模板
a, b = b, a + b
current_num += 1
# 如果在調(diào)用 create_num 的時候,發(fā)現(xiàn)這個函數(shù)中有yield那么此時铝宵,不是調(diào)用函數(shù)打掘,而是創(chuàng)建一個生成器對象。
obj = create_num(10)
print next(obj)
print next(obj)
for num in obj:
print num
# =====================================================
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
yield a
a, b = b, a + b
current_num += 1
return "ok...."
obj2 = create_num(10)
while True:
try:
ret = next(obj2)
print ret
except E xception as ret:
print ret.value # 獲取 return 的值
break
# 使用 send ==================================
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
ret = yield a
print ret # 這里會打印 hahahaha
a, b = b, a + b
current_num += 1
return "ok...."
obj3 = create_num(10)
# obj3.send(None) # send 一般不會放到第一次啟動生成器鹏秋,如果非要這么做尊蚁,那么只能傳遞 None
ret = next(obj)
print ret
# send 里面的數(shù)據(jù)會傳遞給 上面的 ret,當(dāng)做yield的結(jié)果侣夷,然后ret保存這個結(jié)果
# send 的結(jié)果是下一次調(diào)用yield時横朋,yield 左邊的值
ret = obj,send(“hahahaha”)
print ret
yield 實現(xiàn)多任務(wù)
import time
def task_1():
while True:
print "11111"
time.sleep(0.1)
yield
def task_2():
while True:
print "22222"
time.sleep(0.1)
yield
def main():
t1 = task_1()
t2 = task_2()
# 先讓t1執(zhí)行一會,當(dāng)遇到y(tǒng)ield的時候百拓,返回回來
# 再執(zhí)行t2琴锭,當(dāng)t2遇到y(tǒng)ield的時候,再次切換到t1
# 這樣 t1/t2/t1/t2 交替執(zhí)行衙传,最終實現(xiàn)了多任務(wù)----協(xié)程
while True:
next(t1)
next(t2)
if __nam__ == "__main__":
main()
使用 greenlet
import time
from greenlet import greenlet
def test1():
while True:
print "-----A-----"
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print "-------B------"
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 切換到 gr1 中運(yùn)行
gr1.switch()
gevent 使用
greenlet 已經(jīng)實現(xiàn)了協(xié)程决帖,但是這個是人工切換,太麻煩蓖捶。Python還有一個更加強(qiáng)大的模塊 gevent
其原理是當(dāng)一個 greenlet 遇到IO(指的是 input, output, 比如網(wǎng)絡(luò)古瓤,文件操作等)操作時,比如訪問網(wǎng)絡(luò),就自動切換到其他的greenlet落君,等到IO操作完成,再在適當(dāng)?shù)臅r候切換回來繼續(xù)執(zhí)行亭引。
由于IO操作非常耗時绎速,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程焙蚓,就保證總有 greenlet在運(yùn)行纹冤,而不是等待IO。
import time
import gevent
def f1(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
def f2(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
def f3(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()
g2.join()
g3.join()
# ================================================
import time
import gevent
import random
from gevent import monkey
# 有耗時操作時需要
monkey.patch_all() # 將程序中用到的耗時操作代碼购公,換為gevent中自己實現(xiàn)的模塊
def corourine_work(coroutine_name):
for i in range(10):
print coroutine_name, i
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work, "work1"),
gevent.spawn(coroutine_work, "work2")
])