迭代器
迭代是訪問集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對象,迭代器對象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退。
可迭代對象
list,tuple,str,dict等類型的數(shù)據(jù)可以使用for ... in ... 的循環(huán)語法從其中依次拿到數(shù)據(jù)進(jìn)行使用,我們把這樣的過程稱為遍歷,也加迭代。
自己實(shí)現(xiàn)一個(gè)可以迭代的對象
# 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):
''' 如果想要一個(gè)對象可以迭代,那么必須實(shí)現(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
實(shí)現(xiàn)斐波拉契數(shù)列
# coding=utf-8
class FibIterator(object):
'''斐波拉契數(shù)列迭代器'''
def __init__(self, n):
'''
:param n: int, 指明生成數(shù)列的前n個(gè)數(shù)
'''
self.n = n
# current 保存當(dāng)前生成到數(shù)列中的第幾個(gè)數(shù)
self.current = 0
# num1 保存前前一個(gè)數(shù),初始值為數(shù)列中的第一個(gè)數(shù) 0
self.num1 = 0
# num2 保存前一個(gè)數(shù),初始值是數(shù)列中的第二個(gè)數(shù) 1
self.num2 = 1
def __next__(self):
''' 被next()函數(shù)調(diào)用來獲取下一個(gè)數(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()方法)時(shí)按照特定的規(guī)律進(jìn)行生成。但是我們在實(shí)現(xiàn)一個(gè)迭代器時(shí),關(guān)于當(dāng)前迭代到的狀態(tài)需要自己記錄,進(jìn)而才能根據(jù)當(dāng)前狀態(tài)生成下一個(gè)數(shù)據(jù)。為了達(dá)到記錄當(dāng)前狀態(tài),并配合next()函數(shù)進(jìn)行迭代使用,我們可以采用更加簡便的方法---生成器。
生成器是一種特殊的迭代器
創(chuàng)建生成器方法1
要?jiǎng)?chuàng)建一個(gè)生成器,有很多方法。第一種方法,只要把一個(gè)列表生成式的[]改為()
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è)列表,而G是一個(gè)生成器。
# 我們可以直接打印列表L中每一個(gè)元素,而對于生成器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)無法實(shí)現(xiàn)的時(shí)候,還可以使用函數(shù)來實(shí)現(xiàn)。
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
# print a
yield a # 如果一個(gè)函數(shù)中有yield語句,那么就不函數(shù),而是一個(gè)生成器模板
a, b = b, a + b
current_num += 1
# 如果在調(diào)用 create_num 的時(shí)候,發(fā)現(xiàn)這個(gè)函數(shù)中有yield那么此時(shí),不是調(diào)用函數(shù),而是創(chuàng)建一個(gè)生成器對象。
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 # 這里會(huì)打印 hahahaha
a, b = b, a + b
current_num += 1
return "ok...."
obj3 = create_num(10)
# obj3.send(None) # send 一般不會(huì)放到第一次啟動(dòng)生成器,如果非要這么做,那么只能傳遞 None
ret = next(obj)
print ret
# send 里面的數(shù)據(jù)會(huì)傳遞給 上面的 ret,當(dāng)做yield的結(jié)果,然后ret保存這個(gè)結(jié)果
# send 的結(jié)果是下一次調(diào)用yield時(shí),yield 左邊的值
ret = obj,send(“hahahaha”)
print ret
yield 實(shí)現(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í)行一會(huì),當(dāng)遇到y(tǒng)ield的時(shí)候,返回回來
# 再執(zhí)行t2,當(dāng)t2遇到y(tǒng)ield的時(shí)候,再次切換到t1
# 這樣 t1/t2/t1/t2 交替執(zhí)行,最終實(shí)現(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)實(shí)現(xiàn)了協(xié)程,但是這個(gè)是人工切換,太麻煩。Python還有一個(gè)更加強(qiáng)大的模塊 gevent
其原理是當(dāng)一個(gè) greenlet 遇到IO(指的是 input, output, 比如網(wǎng)絡(luò),文件操作等)操作時(shí),比如訪問網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來繼續(xù)執(zhí)行。
由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(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
# 有耗時(shí)操作時(shí)需要
monkey.patch_all() # 將程序中用到的耗時(shí)操作代碼,換為gevent中自己實(shí)現(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")
])