迭代是Python最強大的功能之一,是訪問集合元素的一種方式。python
迭代器是一個能夠記住遍歷的位置的對象。shell
迭代器對象從集合的第一個元素開始訪問,直到全部的元素被訪問完結束。迭代器只能往前不會後退。數據庫
迭代器有兩個基本的方法:iter() 和 next()。編程
字符串,列表,元組,集合、字典、range()、文件句柄等可迭代對象(iterable)均可用於建立迭代器:多線程
>>> list = [1,2,3,4] >>> it = iter(list) # 建立迭代器對象 >>> next(it) # 輸出迭代器的下一個元素 1 >>> next(it) 2 >>>
迭代器對象可使用常規for語句進行遍歷:併發
>>> list = ['a', 'b', 'c', 'd'] >>> it = iter(list) # 建立迭代器對象 >>> for x in it: print(x, end=" ") a b c d >>>
也可使用 next() 函數:app
>>> lst = [2,6,8,9] >>> it = iter(lst) # 建立迭代器對象 >>> >>> while True: try: print(next(it)) except StopIteration: break 2 6 8 9 >>>
把一個類做爲一個迭代器使用須要在類中實現兩個方法 __iter__() 與 __next__() 。ssh
若是你已經瞭解的面向對象編程,就知道類都有一個構造函數,Python 的構造函數爲 __init__(), 它會在對象初始化的時候執行。異步
__iter__() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 __next__() 方法並經過 StopIteration 異常標識迭代的完成。 __next__() 方法(Python 2 裏是 next())會返回下一個迭代器對象。
建立一個返回數字的迭代器(計數器),初始值爲 1,逐步遞增 1:async
class Counter: def __iter__(self): self.a = 1 return self def __next__(self): x = self.a self.a += 1 return x myclass = Counter() myiter = iter(myclass) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter))
# 執行輸出結果爲: 1 2 3 4 5
StopIteration 異經常使用於標識迭代的完成,防止出現無限循環的狀況,在 __next__() 方法中咱們能夠設置在完成指定循環次數後觸發 StopIteration 異常來結束迭代。
>>> str1 = "Python" >>> strObj = str1.__iter__() >>> strObj.__next__() 'P' >>> strObj.__next__() 'y' >>> strObj.__next__() 't' >>> strObj.__next__() 'h' >>> strObj.__next__() 'o' >>> strObj.__next__() 'n' >>> strObj.__next__() Traceback (most recent call last): File "<pyshell#33>", line 1, in <module> strObj.__next__() StopIteration >>>
那麼如何判斷一個對象是不是可迭代對象?
>>> tup = (1,2,3) >>> type(tup) <class 'tuple'> >>> dir(tup) # 帶參數時,返回參數的屬性、方法列表。 ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__',
'__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] >>> print('__iter__' in dir(tup)) True >>>
>>> dic = {1:'dict', 2:'str', 3:'list', 4:'tuple', 5:'set', 6:'range()',7:'flie handler'} >>> isinstance(dic, Iterable) True >>> isinstance(dic, Iterator) False >>> >>> ran = range(6) >>> type(ran) <class 'range'> >>> isinstance(ran, Iterable) True >>> isinstance(ran, Iterator) False >>>
在 Python 中,使用了 yield 的函數被稱爲生成器(generator)。
跟普通函數不一樣的是,生成器是一個返回迭代器的函數,只能用於迭代操做,更簡單點理解生成器就是一個迭代器。
在調用生成器運行的過程當中,每次遇到 yield 時函數會暫停並保存當前全部的運行信息,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續運行。
調用一個生成器函數,返回的是一個迭代器對象。
yield Vs return:
return返回後,函數狀態終止,而yield會保存當前函數的執行狀態,在返回後,函數又回到以前保存的狀態繼續執行。
如下實例使用 yield 實現斐波那契數列:
>>> def fib(max): # 生成器函數 - 斐波那契 a, b, n = 0, 1, 0 while n < max: yield b # 使用 yield a, b = b, a + b n = n + 1 >>> f = fib(6) # 調用 fab(5) 不會執行 fab 函數,而是返回一個 iterable 對象! >>> f # Python 解釋器會將其視爲一個 generator <generator object fib at 0x000001C6CB627780> >>> >>> for n in fib(5): print(n) 1 1 2 3 5 >>> >>> f = fib(5) >>> next(f) # 使用next函數從生成器中取值,使用next能夠推進生成器的執行 1 >>> next(f) 1 >>> next(f) 2 >>> next(f) 3 >>> next(f) 5 >>> next(f) # 當函數中已經沒有更多的yield時繼續執行next(g),遇到StopIteration Traceback (most recent call last): File "<pyshell#37>", line 1, in <module> next(f) StopIteration >>> >>> fwrong = fib(6) >>> fwrong.next() # Python2 中的語法,Python3 會報錯 Traceback (most recent call last): File "<pyshell#40>", line 1, in <module> fwrong.next() # Python2 中的語法,Python3 會報錯 AttributeError: 'generator' object has no attribute 'next' >>>
send向生成器中發送數據。send的做用至關於next,只是在驅動生成器繼續執行的同時還能夠向生成器中傳遞數據。
>>> import numbers >>> def gen_sum(): total = 0 while True: num = yield if isinstance(num, numbers.Integral): total += num print('total: ', total) elif num is None: break return total >>> g = gen_sum() >>> g <generator object gen_sum at 0x0000026A6703D3B8> >>> g.send(None) # 至關於next(g),預激活生成器 >>> g.send(2) total: 2 >>> g.send(6) total: 8 >>> g.send(12) total: 20 >>> g.send(None) # 中止生成器 Traceback (most recent call last): File "<pyshell#40>", line 1, in <module> g.send(None) StopIteration: 20 >>> >>> try: g.send(None) # 中止生成器 except StopIteration as e: print(e.value) None >>>
yield from 將一個可迭代對象變成一個迭代器返回,也能夠說,yield from關鍵字能夠直接返回一個生成器
>>> def func(): lst = ['str', 'tuple', 'list', 'dict', 'set'] yield lst >>> gen = func() >>> next(gen) ['str', 'tuple', 'list', 'dict', 'set'] >>> for i in gen: print(i) >>> # yield from 將一個可迭代對象變成一個迭代器返回 >>> def func2(): lst = ['str', 'tuple', 'list', 'dict', 'set'] yield from lst >>> gen2 = func2() >>> next(gen2) 'str' >>> next(gen2) 'tuple' >>> for i in gen2: print(i) list dict set >>>
>>> lst = ['H','e','l'] >>> dic = {'l':'vvvvv','o':'eeeee'} >>> str1 = 'Python' >>> >>> def yield_gen(): for i in lst: yield i for j in dic: yield j for k in str1: yield k >>> for item in yield_gen(): print(item, end='') HelloPython >>> >>> l = ['H','e','l'] >>> d = {'l':'xxxxx','o':'ooooo'} >>> s = 'Java' >>> >>> def yield_from_gen(): yield from l yield from d yield from s >>> for item in yield_from_gen(): print(item, end='') HelloJava >>>
更容易使用,代碼量較小內存使用更加高效。好比:
根據維基百科給出的定義,「協程 是爲非搶佔式多任務產生子程序的計算機程序組件,協程容許不一樣入口點在不一樣位置暫停或開始執行程序」。從技術的角度來講,「協程就是你能夠暫停執行的函數」。若是你把它理解成「就像生成器同樣」,那麼你就想對了。
協程,又稱微線程,纖程。英文名Coroutine。
協程的概念很早就提出來了,但直到最近幾年纔在某些語言(如Lua)中獲得普遍應用。
# 與多線程、多進程等併發模型不一樣,協程依靠user-space調度,而線程、進程則是依靠kernel來進行調度。 # 線程、進程間切換都須要從用戶態進入內核態,而協程的切換徹底是在用戶態完成,且不像線程進行搶佔式調度,協程是非搶佔式的調度。 # 一般多個運行在同一調度器中的協程運行在一個線程內,這也消除掉了多線程同步等帶來的編程複雜性。同一時刻同一調度器中的協程只有一個會處於運行狀態,這一點很容易從前言得出。 一個一般的誤解是協程不能利用CPU的多核心,經過利用多個線程多個調度器,協程也是能夠用到CPU多核心性能的。
協程的定義 協程最先的描述是由Melvin Conway於1958給出「subroutines who act as the master program」(與主程序行爲相似的子例程),此後他又在博士論文中給出了以下定義: · the values of data local to a coroutine persist between successive calls(協程的局部數據在後續調用中始終保持) · the execution of a coroutine is suspended as control leaves it, only to carry on where it left off when control re-enters the coroutine at some later stage (當控制流程離開時,協程的執行被掛起,此後控制流程再次進入這個協程時,這個協程只應從上次離開掛起的地方繼續)。
協程的特色在因而一個線程執行,那和多線程比,協程有何優點?
由於協程是一個線程執行,那怎麼利用多核CPU呢?
#基於yield實現異步 def consumer(): '''任務1:接收數據,處理數據''' while True: x=yield def producer(): '''任務2:生產數據''' g=consumer() next(g) for i in range(10000000): g.send(i) producer()
import datetime import heapq # 堆模塊 import time class Task: def __init__(self, wait_until, coro): self.coro = coro self.waiting_until = wait_until def __eq__(self, other): return self.waiting_until == other.waiting_until def __lt__(self, other): return self.waiting_until < other.waiting_until class SleepingLoop: def __init__(self, *coros): self._new = coros self._waiting = [] def run_until_complete(self): for coro in self._new: wait_for = coro.send(None) heapq.heappush(self._waiting, Task(wait_for, coro)) while self._waiting: now = datetime.datetime.now() task = heapq.heappop(self._waiting) if now < task.waiting_until: delta = task.waiting_until - now time.sleep(delta.total_seconds()) now = datetime.datetime.now() try: print('*'*50) wait_until = task.coro.send(now) print('-'*50) heapq.heappush(self._waiting, Task(wait_until, task.coro)) except StopIteration: pass def sleep(seconds): now = datetime.datetime.now() wait_until = now + datetime.timedelta(seconds=seconds) print('before yield wait_until') actual = yield wait_until # 返回一個datetime數據類型的時間 print('after yield wait_until') return actual - now def countdown(label, length, *, delay=0): print(label, 'waiting', delay, 'seconds before starting countdown') delta = yield from sleep(delay) print(label, 'starting after waiting', delta) while length: print(label, 'T-minus', length) waited = yield from sleep(1) length -= 1 print(label, 'lift-off!') def main(): loop = SleepingLoop(countdown('A', 5), countdown('B', 3, delay=2), countdown('C', 4, delay=1)) start = datetime.datetime.now() loop.run_until_complete() print('Total elapsed time is', datetime.datetime.now() - start) if __name__ == '__main__': main()
執行結果:
A waiting 0 seconds before starting countdown before yield wait_until B waiting 2 seconds before starting countdown before yield wait_until C waiting 1 seconds before starting countdown before yield wait_until ************************************************** after yield wait_until A starting after waiting 0:00:00 A T-minus 5 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until C starting after waiting 0:00:01.001511 C T-minus 4 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until A T-minus 4 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until B starting after waiting 0:00:02.000894 B T-minus 3 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until C T-minus 3 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until A T-minus 3 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until B T-minus 2 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until C T-minus 2 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until A T-minus 2 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until B T-minus 1 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until C T-minus 1 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until A T-minus 1 before yield wait_until -------------------------------------------------- ************************************************** after yield wait_until B lift-off! ************************************************** after yield wait_until C lift-off! ************************************************** after yield wait_until A lift-off! Total elapsed time is 0:00:05.005168
asyncio
是Python 3.4版本引入的標準庫,直接內置了對異步IO的支持。
用asyncio
提供的@asyncio.coroutine
能夠把一個generator標記爲coroutine類型,而後在coroutine內部用yield from
調用另外一個coroutine實現異步操做。
asyncio
的編程模型就是一個消息循環。咱們從asyncio
模塊中直接獲取一個EventLoop
的引用,而後把須要執行的協程扔到EventLoop
中執行,就實現了異步IO。
coroutine+yield from
import asyncio @asyncio.coroutine def hello(): print("Nice to learn asyncio.coroutine!") # 異步調用asyncio.sleep(1): r = yield from asyncio.sleep(1) print("Nice to learn asyncio.coroutine again !") # 獲取EventLoop: loop = asyncio.get_event_loop() # 執行coroutine loop.run_until_complete(hello()) loop.close()
Nice to learn asyncio.coroutine ! Nice to learn asyncio.coroutine again !
爲了簡化並更好地標識異步IO,從Python 3.5開始引入了新的語法async
和await
,可讓coroutine的代碼更簡潔易讀。
請注意,async
和 await
是針對coroutine的新語法,要使用新的語法,只須要作兩步簡單的替換:
@asyncio.coroutine
替換爲async
;yield from
替換爲await
。async+await
在協程函數中,能夠經過await語法來掛起自身的協程,並等待另外一個協程完成直到返回結果:
import asyncio async def hello(): print("Nice to learn asyncio.coroutine!") # 異步調用asyncio.sleep(1): await asyncio.sleep(1) print("Nice to learn asyncio.coroutine again !") # 獲取EventLoop: loop = asyncio.get_event_loop() # 執行coroutine loop.run_until_complete(hello()) loop.close()
執行多個任務
import threading import asyncio async def hello(): print('Hello Python! (%s)' % threading.currentThread()) await asyncio.sleep(1) print('Hello Python again! (%s)' % threading.currentThread()) loop = asyncio.get_event_loop() tasks = [hello(), hello()] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
結果:
Hello Python! (<_MainThread(MainThread, started 4536)>) Hello Python! (<_MainThread(MainThread, started 4536)>) Hello Python again! (<_MainThread(MainThread, started 4536)>) Hello Python again! (<_MainThread(MainThread, started 4536)>)
獲取返回值
import threading import asyncio async def hello(): print('Hello Python! (%s)' % threading.currentThread()) await asyncio.sleep(1) print('Hello Python again! (%s)' % threading.currentThread()) return "It's done" loop = asyncio.get_event_loop() task = loop.create_task(hello()) loop.run_until_complete(task) ret = task.result() print(ret)
結果:
Hello Python! (<_MainThread(MainThread, started 6136)>) Hello Python again! (<_MainThread(MainThread, started 6136)>) It's done
執行多個任務獲取返回值
import threading import asyncio async def hello(seq): print('Hello Python! (%s)' % threading.currentThread()) await asyncio.sleep(1) print('Hello Python again! (%s)' % threading.currentThread()) return "It's done", seq loop = asyncio.get_event_loop() task1 = loop.create_task(hello(2)) task2 = loop.create_task(hello(1)) task_list = [task1, task2] tasks = asyncio.wait(task_list) loop.run_until_complete(tasks) for t in task_list: print(t.result())
結果:
Hello Python! (<_MainThread(MainThread, started 12956)>) Hello Python! (<_MainThread(MainThread, started 12956)>) Hello Python again! (<_MainThread(MainThread, started 12956)>) Hello Python again! (<_MainThread(MainThread, started 12956)>) ("It's done", 2) ("It's done", 1)