硬着頭皮看了一週的asyncio模塊代碼,瞭解了大概的執行流程,引用太多,成尤爲是對象間函數的引用。python
光是這麼一段簡單的代碼:數組
# coding: utf8
import asyncio import random # 這個裝飾器沒作什麼,對於生成器來講,只是爲函數加個屬性 _is_coroutine = True
@asyncio.coroutine def smart_fib(n): index = 0 a = 0 b = 1
while index < n: sleep_secs = random.uniform(0, 0.2) yield from asyncio.sleep(5) print('Smart one think {} secs to get {}'.format(sleep_secs, b)) a, b = b, a + b index += 1
if __name__ == '__main__': loop = asyncio.get_event_loop() tasks = [ # async返回一個Task實例
# Task實例化時, task內部的_step函數包裹了傳入的coroutine, 調用loop的call_soon方法, 傳入_step函數
# call_soon方法以傳入的_step函數建立一個Handle實例, 再在self._ready隊列中加入這個Handle實例
asyncio.async(smart_fib(2)), ] loop.run_until_complete(asyncio.wait(tasks)) print('All fib finished.') loop.close()
後面牽扯出的類就在這麼多個:網絡
Task包裹generator,Handle又包裹Task裏的_step方法,loop的隊列又包含Handle對象,loop的堆裏又包含TimerHandle對象,還要把堆裏的彈出,放入隊列,而後又開始一輪新的select事件循環。app
整個時序圖畫起來複雜,仍是撿點小魚蝦來得實在。dom
如下都是在Python3.4下的socket
socketpairasync
def _socketpair(self): return socket.socketpair()
socketpair會建立兩個網絡文件系統的描述符socket[0]、socket[1] ,保存在一個二元數組中。用於雙向的數據傳輸。.相似於一根雙向的管道,socket[0]、socket[1] 均可讀寫:
—— 在socket[0]寫入,只能在socket[1]讀出
—— 也可在 socket[0] 讀取 socket[1] 寫入的數據函數
接收以下:oop
self._ssock, self._csock = self._socketpair() self._ssock.setblocking(False) self._csock.setblocking(False)
ABCMeta性能
class BaseSelector(metaclass=ABCMeta): @abstractmethod def register(self, fileobj, events, data=None): pass
實現抽象的類,繼承的時候要覆蓋這個方法
判斷是不是函數:
def isfunction(object): """Return true if the object is a user-defined function."""
return isinstance(object, types.FunctionType)
在Lib/inspect.py模塊裏
判斷是否方法是一個實例方法:
def ismethod(object): """Return true if the object is an instance method."""
return isinstance(object, types.MethodType)
判斷是不是一個生成器(generator function):
def isgeneratorfunction(object): """Return true if the object is a user-defined generator function."""
return bool((isfunction(object) or ismethod(object)) and object.__code__.co_flags & 0x20)
使用iter方法:
>>> i = iter('abc') >>> i.next() 'a'
>>> i.next() 'b'
>>> i.next() 'c'
>>> i.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
理解了yield,和yield from的用法
select模型
select有阻塞與非阻塞兩種方式:若是提供了timeout時間數,則會最多阻塞timeout秒,若是在這timeout秒內監聽到文件描述符有變更,則當即返回;不然一直阻塞直到timeout秒。
非阻塞:輪詢,間隔詢問。select.select(self.inputs, self.outputs, self.inputs, 1)
阻塞:阻塞等待返回。select.select(self.inputs, self.outputs, self.inputs)
缺點:由於要監聽的是文件描述符,因此存在最大描述符限制。默認狀況下,單個進程最大能監視1024個文件描述符;
採用的是輪詢的方式掃描文件描述符,數量越多,性能越差;
select返回的是整個句柄的數組,須要遍歷整個數組,不對針對某個特定句柄。
Poll模型
簡單來講,poll是使用鏈表保存文件描述符,所以沒有了監視文件數量的限制。但select的其餘缺點,poll也有。
Epoll模型
根據每一個fd上的callback函數來實現,只有活躍的socket纔會主動調用callback,再也不輪詢。
monotonic time
在初始化BaseEventLoop時,有這麼一句時間語句:
self._clock_resolution = time.get_clock_info('monotonic').resolution
monotonic time字面意思是單調時間,實際上它指的是系統啓動之後流逝的時間,這是由變量jiffies來記錄的。系統每次啓動時jiffies初始化爲0,每來一個timer interrupt,jiffies加1,也就是說它表明系統啓動後流逝的tick數。jiffies必定是單調遞增的,不能人爲減少,除非從新啓動!
(參考資料: http://blog.csdn.net/tangchenchan/article/details/47989473 )
同時,有下列幾種時間:
clock': time.clock()
'monotonic': time.monotonic() 'perf_counter': time.perf_counter() 'process_time': time.process_time() 'time': time.time()
例子:
#python 3.4
import time print(time.get_clock_info('clock'))
結果輸出以下:
namespace(adjustable=False, implementation='QueryPerformanceCounter()', monotonic=True, resolution=3.20731678764131e-07)
(參考資料: http://blog.csdn.net/caimouse/article/details/51750982 )
因此 time.get_clock_info('monotonic').resolution 就是獲取系統啓動後,此時的時間值。
random.uniform函數:
sleep_secs = random.uniform(0, 0.2)
uniform() 方法將隨機生成下一個實數,它在 [x, y) 範圍內。
#!/usr/bin/python # -*- coding: UTF-8 -*-
import random print "uniform(5, 10) 的隨機數爲 : ", random.uniform(5, 10) print "uniform(7, 14) 的隨機數爲 : ", random.uniform(7, 14)
以上實例運行後輸出結果爲:
uniform(5, 10) 的隨機數爲 : 6.98774810047 uniform(7, 14) 的隨機數爲 : 12.2243345905
堆
堆一般是一個能夠被看作一棵樹的數組對象。堆老是知足下列性質:
也就是說,堆頂老是最大值或者最小值。
python的heapq模塊提供了堆操做:
heap = [] #建立了一個空堆 heappush(heap,item) #往堆中插入一條新的值
finally無論錯誤有沒有拋出,都會執行:
try: raise Exception('hahah') finally: print("finally") print("end")
輸出:finally
異常
__str__和__repr__的區別
print a時,優先使用__str__方法,沒有__str__方法時,纔會調用__repr__方法;
在命令行模式下,直接>>a 調用的是__repr方法
輸出引用方法時:
B類裏引用了A類的一個方法,在B類的輸出A類的方法,會調用A的__repr__方法,這就解釋了爲何在loop._shedule裏,輸出TimerHandle時,會跳到Future的__repr__方法。
下面這個例子很好的說明了這種狀況:
class Test: def __init__(self, callback): self.a = 11 self._callback = callback def __repr__(self): res = 'TimerHandle({}, {})'.format(self.a, self._callback) return res class Test2: def __init__(self): self.a = 1
def aa(self): return 'shit' def __repr__(self): res = 'what the shit' return res def __str__(self): return "test2" a = Test2() t = Test(a.aa) print(t)
輸出以下:
>>> TimerHandle(11, <bound method Test2.aa of what the shit>) >>>
raise語句會向上拋出異常,異常不會被同一做用域的except捕獲, 由於raise語句自己沒有報錯。下面的例子要以看出:
class _StopError(BaseException): pass
class T: def run(self): try: raise _StopError except Exception as exc: print("hahahah") class B: def run_once(self, t): t.run() def run_forever(self, t): try: self.run_once(t) except _StopError: print("xixixxixi") t = T() b = B() b.run_forever(t)
輸出:xixixixixi
判斷python的版本:
_PY34 = sys.version_info >= (3, 4)