从range和xrange的性能对比到yield关键字(中)
2019獨角獸企業重金招聘Python工程師標準>>>
上節提出了range和xrange的效率問題,這節我們來探究其中的原因
?
yield的使用
?
我們看下面的程序:
#coding: utf-8def test():print 4print 2print 5if __name__ == '__main__':test()這段代碼的運行結果當然是沒有任何疑問的。
但是如果我將代碼修改一下:
#coding: utf-8def test():yield 4yield 2yield 5if __name__ == '__main__':print test()運行結果有些奇怪:
<generator object test at 0xb71f1144>我們嘗試這樣使用:
if __name__ == '__main__':for i in test():print i結果卻出人意料:
wing@ubuntu:~/Documents/py|? python 17.py 4 2 5這是什么原因呢?這里看起來,test()好像一個集合,里面存儲了4,2,5,所以我們才能夠依次遍歷。
實際上,原因并非如此。
當一個函數中含有yield時,這個函數就不再是一個普通的函數,而是一個可迭代的對象(實際上叫做生成器,不過現在不必關心概念)。
同樣,執行該函數時,不再是馬上執行其中的語句,而是生成一個可迭代對象。當執行迭代的時候,才真正運行其中的代碼。
當函數體執行到yield時,便退出這個函數,此時yield具有return的功能。但是這里的關鍵是,當下次執行這個函數時,并不是從頭開始執行,而是從上次yield退出的位置繼續執行。
嘗試下面的代碼:
#coding: utf-8def test():yield 4yield 2yield 5if __name__ == '__main__':t = test()it = iter(t)print it.next()print it.next()print it.next()print it.next()運行結果為:
wing@ubuntu:~/Documents/py|? python 17.py 4 2 5 Traceback (most recent call last):File "17.py", line 14, in <module>print it.next() StopIteration從這里的結果可以看出,test()語句沒有執行代碼段,而是生成了一個可以迭代的對象。
我們甚至可以得出結論,每當執行一次next,就向后執行到下一個yield語句,或者所有的語句執行完畢。
?
range的實現
?
我們嘗試實現range:
#coding: utf-8def _range(value):i = 0result = []while i < value:result.append(i)i += 1return resultif __name__ == '__main__':for i in _range(4):print irange的邏輯比較簡單,就是生成一個列表。
?
xrange的模擬實現
?
我們根據前面的結論,猜測xrange是一個含有yield的函數,于是:
#coding: utf-8def _xrange(value):i = 0while i < value:yield ii += 1if __name__ == '__main__':for i in _xrange(4):print i運行一下,結果和我們預期一致。
當然,實際的xrange比我們這里編寫的更加復雜,但是基本原理是一致的。
?
為何xrange比range高效?
?
答案很明顯了,range是一次性生成所有的數據,而xrange,內部使用了yield關鍵字,每次只運行其中一部分,這樣從頭到尾都沒有占用大量的內存和時間。所以效率較高。
?
我們再次比較性能,這次比較的是我們自己編寫的版本:
#coding: utf-8 import sys from time import timedef _range(value):i = 0result = []while i < value:result.append(i)i += 1return resultdef _xrange(value):i = 0while i < value:yield ii += 1def count_time(func):def wrapped(*args, **kargs):begin_time = time()result = func(*args, **kargs)end_time = time()cost_time = end_time - begin_timeprint '%s called cost time : %s ms' %(func.__name__, float(cost_time)*1000)return resultreturn wrapped@count_time def test1(length):for i in _range(length):pass@count_time def test2(length):for i in _xrange(length):passif __name__ == '__main__':length = int(sys.argv[1])test1(length)test2(length)運行結果為:
wing@ubuntu:~/Documents/py|? python 19.py 1000 test1 called cost time : 0.116109848022 ms test2 called cost time : 0.0619888305664 ms wing@ubuntu:~/Documents/py|? python 19.py 10000 test1 called cost time : 2.39086151123 ms test2 called cost time : 0.566959381104 ms wing@ubuntu:~/Documents/py|? python 19.py 100000 test1 called cost time : 15.5799388885 ms test2 called cost time : 6.41298294067 ms wing@ubuntu:~/Documents/py|? python 19.py 1000000 test1 called cost time : 130.295038223 ms test2 called cost time : 65.4468536377 ms wing@ubuntu:~/Documents/py|? python 19.py 10000000 test1 called cost time : 13238.3038998 ms test2 called cost time : 652.212142944 ms顯然,使用yield的版本更加高效。
?
下文,我們探究生成器。
轉載于:https://my.oschina.net/inevermore/blog/388651
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的从range和xrange的性能对比到yield关键字(中)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDR 拍照模式的原理,实现及应用
- 下一篇: IOS开发-GitHub使用详解