最近,我讀了一篇有趣的文章,文中介紹了一些未充分使用的Python特性的,在文章中,作者提到,從Python 3.2開始,標準庫附帶了一個內置的裝飾器functools.lru_cache,我發現這個裝飾器很令人興奮,有了它,我們有可能輕松地為許多應用程式加速,
你可能在想,這很好,但這個裝飾器究竟是什么?它提供對已構建的快取的訪問,該快取使用LRU(譯者注: Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰,)的置換策略,因此被命名為lru_cache,當然,這句話聽起來可能有點令人膽怯,所以讓我們把它分解一下,
這里也要注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
什么是快取?
快取是一個可以快速訪問的地方,可以在它里面存盤訪問速度較慢的內容,為了演示這一點,讓我們以你的web瀏覽器為例,
從網路上讀取網頁可能需要幾秒鐘,即使是快速的網路連接也如此,在計算機時代,這個問題是永恒的,為了解決這個問題,瀏覽器將你已經訪問過的網頁存盤在計算機的快取中,這樣訪問速度會加快數千倍,
使用快取下載網頁的步驟如下:
- 檢查頁面的本地快取,如果頁面在那里,回傳該頁面,
- 在因網上找到網頁并從那里下載,
- 將該網頁存盤在快取中,以便將來更快地訪問,
雖然快取并不會讓你第一次訪問網頁的速度加快,但通常你是要屢次訪問某一個網站頁面的(想想Facebook——注:對多數國人來講,可能不是這個網站,或者你的電子郵件),有了快取之后,以后每次訪問都會更快,
瀏覽器并不是唯一使用快取的,從服務器到CPU和硬碟或SSD之間的計算機硬體,它們無處不在,從快取中可以很快地獲取資料,因此當你不止一次獲取資料時,它可以大大加快程式的速度,
LRU是什么意思?
快取只能存盤有限數量的東西,而且通常它比可能存入所快取的東西要小得多(例如,你的硬碟比互聯網小得多),這意味著有時需要將快取中已有內容替換掉,放入其他內容,對于去掉什么的決策方法被稱為置換策略,
這就是LRU的用武之地,LRU代表最近用得最少的快取中內容,這是一種常用的快取置換策略,
為什么置換策略很重要?
“最近使用最少”這種置換策略的基本思想是:如果你有一段時間沒有訪問過某個東西,你可能近期不會訪問它,要使用此策略,只需在快取已滿時洗掉最早使用的項即可,
在上圖中,快取中的每個項都附帶了訪問時間,依據LRU策略,選擇訪問時間為2:55PM 的項作為要置換的項,因為它是最早被訪問的,如果有兩個物件具有相同的訪問時間,那么LRU將從中隨機選擇一個,
這種去掉長時間不用的東西的策略,被稱為Bélády的最優演算法,它是置換快取內容的最佳策略,當然,我們根本不知道未來會有什么操作,謝天謝地,在許多情況下,LRU提供了近乎最佳的性能,
怎樣使用它?
functools.lru_cache是一個裝飾器,因此你可以將它放在函式的頂部:
import functools
@functools.lru_cache(maxsize=128)
def fib(n):
if n < 2:
return 1
return fib(n-1) + fib(n-2)
復制代碼
Fibonacci數列在遞回示例中經常被用到,要提升這個函式的速度,使用functools.lru_cache之后,不費吹灰之力,就能讓這個遞回函式狂飆,在我的機器上運行這些代碼,得到了這個函式有快取版本和沒有快取版本的以下結果,
$ python3 -m timeit -s 'from fib_test import fib' 'fib(30)'
10 loops, best of 3: 282 msec per loop
$ python3 -m timeit -s 'from fib_test import fib_cache' 'fib_cache(30)'
10000000 loops, best of 3: 0.0791 usec per loop
復制代碼
增加一行代碼之后,速度提高了3565107倍,
當然,我認為很難看出你在實際中會如何使用它,因為我們很少需要計算斐波那契數列,回到web頁面示例,我們可以舉一個更實際的用快取渲染前端模板的例子,
在服務器開發中,通常單個頁面存盤為具有占位符變數的模板,例如,下面是一個頁面模板,該頁面顯示某一天各種足球比賽的結果,
<html>
<body>
<h1>Matches for {{day}}</h1>
<table id="matches">
<tr>
<td>Home team</td>
<td>Away team</td>
<td>Score</td>
</tr>
{% for match in matches %}
<tr>
<td>{{match["home"]}}</td>
<td>{{match["away"]}}</td>
<td>{{match["home_goals"]}} - {{match["away_goals"]}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
復制代碼
呈現模板時,看起來如下所示:
這是快取的主要目標,因為每天的結果不會改變,而且很可能每天會有多次訪問,下面是一個提供此模板的Flask應用程式,我引入了50ms的延遲來模擬通過網路或者從大型資料庫獲取匹配字典,
import json
import time
from flask import Flask, render_template
app = Flask(__name__)
with open('match.json','r') as f:
match_dict = json.load(f)
def get_matches(day):
# simulate network/database delay
time.sleep(0.05)
return match_dict[day]
@app.route('/matches/<day>')
def show_matches(day):
matches = get_matches(day)
return render_template('matches.html', matches=matches, day=day)
if __name__ == "__main__":
app.run()
復制代碼
使用requests在不快取的情況下獲得三天的資料,在我的計算機上本地運行平均需要171ms,這還不錯,但我們可以做得更好,即使考慮到人為的延遲,
@app.route('/matches/<day>')
@functools.lru_cache(maxsize=4)
def show_matches(day):
matches = get_matches(day)
return render_template('matches.html', matches=matches, day=day)
復制代碼
在本例中,我設定了maxsize=4,因為我的測驗腳本只有相同的三天,最好設定2次冪,使用這種方法,10個回圈的平均速度可以降到13.7ms,
還有什么應該知道?
Python檔案雖然很詳細,但是有一些東西還是要強調的,
內置函式
裝飾器附帶了一些很有用的內置函式,cache_info()回傳訪問數(hits)、未訪問數(misses)和當前快取使用量(currsize)、最大容量(maxsize),幫助你了解快取使用情況,cache_clear()將洗掉快取中的所有元素,
有時候不要使用快取
通常,只有在以下情況下才能使用快取:
- 在快取期內,資料不會更改,
- 函式將始終為相同的引數回傳相同的值(因此時間和隨機對快取沒有意義),
- 函式沒有副作用,如果快取被訪問,則永遠不會呼叫該函式,因此請確保不更改其中的任何狀態,
- 函式不回傳不同的可變物件,例如,回傳串列的函式不適合快取,因為將要快取的是對串列的參考,而不是串列內容,
最后注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/150254.html
標籤:Python
上一篇:新浪微博python爬蟲分享(一天可抓取 1300 萬條資料),超級無敵
下一篇:Flask-基本使用
