對於經常呼叫的函式,特別是遞迴函式或計算密集的函式,記憶(快取)返回值可以顯著提高效能。而在 python 裡,可以使用字典來完成。
下面這個計算斐波那契數列的函式fib()
具有記憶功能,對於計算過的函式引數可以直接給出答案,不必再計算:
fib_memo = {}
def fib(n):
if n < 2: return 1
if not n in fib_memo:
fib_memo[n] = fib(n-1) + fib(n-2)
return fib_memo[n]
我們可以把這個操作包裝成乙個類memory
,這個類的物件都具有記憶功能:
class memoize:
"""memoize(fn) - 乙個和 fn 返回值相同的可呼叫物件,但它具有額外的記憶功能。
只適合引數為不可變物件的函式。
"""def __init__(self, fn):
self.fn = fn
self.memo = {}
def __call__(self, *args):
if not args in self.memo:
self.memo[args] = self.fn(*args)
return self.memo[args]
# 原始函式
def fib(n):
print(f'calculating fib()')
if n < 2: return 1
return fib(n-1) + fib(n-2)
# 使用方法
fib = memoize(fib)
執行測試,計算兩次fib(10)
:
calculating fib(10)
calculating fib(9)
calculating fib(8)
calculating fib(7)
calculating fib(6)
calculating fib(5)
calculating fib(4)
calculating fib(3)
calculating fib(2)
calculating fib(1)
calculating fib(0)
8989
可以看到第二次直接輸出 89,沒有經過計算。
對裝飾器熟悉的程式設計師應該已經想到,這個類可以被當成裝飾器使用。在定義fib()
的時候可以直接這樣:
@memoize
def fib(n):
if n < 2: return 1
return fib(n-1) + fib(n-2)
這和之前的**等價,但是更簡潔明瞭。
之前的memory
類只適合包裝引數為不可變物件的函式。原因是我們用到了字典作為儲存介質,將引數作為字典的 key;而在 python 中的 dict 只能把不可變物件作為 key 2,例如數字、字串、元組(裡面的元素也得是不可變物件)。所以提高**通用性,我們只能犧牲執行速度,將函式引數序列化為字串再作為 key 來儲存,如下:
class memoize:
"""memoize(fn) - 乙個和 fn 返回值相同的可呼叫物件,但它具有額外的記憶功能。
此時適合所有函式。
"""def __init__(self, fn):
self.fn = fn
self.memo = {}
def __call__(self, *args):
import pickle
s = pickle.dumps(args)
if not s in self.memo:
self.memo[s] = self.fn(*args)
return self.memo[s]
除了這種手工製作的方法,有乙個第三方庫 joblib 能實現同樣的功能,而且效能更好,適用性更廣。因為上文中的方法是快取在記憶體中的,每次都要比較傳入的引數。對於很大的物件作為引數,如 numpy 陣列,這種方法效能很差。而 joblib.memory 模組提供了乙個儲存在硬碟上的memory
類,其用法如下:
首先定義快取目錄:
>>> cachedir = 'your_cache_location_directory'
以此快取目錄建立乙個 memory 物件:
>>> from joblib import memory
>>> memory = memory(cachedir, verbose=0)
使用它和使用裝飾器一樣:
>>> @memory.cache
... def f(n):
... print(f'running f()')
... return x
以同樣的引數執行這個函式兩次,只有第一次會真正計算:
>>> print(f(1))
running f(1)
1>>> print(f(1))
1
1
2 3
(本文完)
python 函式返回值
帶有返回值的函式 def add2num a,b c a b return c或者 def add2num a,b return a b在本小節剛開始的時候,說過的 買菸 的例子中,最後兒子給你菸時,你一定是從兒子手中接過來 對麼,程式也是如此,如果乙個函式返回了乙個資料,那麼想要用這個資料,那麼就...
python 函式返回值
python 函式返回值有兩種形式 1 返回乙個值。2 返回多個值。現看看返回乙個值的吧。deffirstvalue a b c a b return cprint firstvalue 1 2 結果 3 再看看返回多個值的 那怎麼可以返回多個值呢,其他的語言一般呼叫函式的話,只能返回乙個值,可能我...
python 函式返回值
函式返回值 return 1 返回乙個值 return result 2 返回多個值 如果返回多個資料,資料之間使用逗號進行分割,那麼返回的是元組型別 return first num,second num,result 3 注意 函式中有return,函式執行到return,函式一定會中斷 如果沒...