記憶(緩存)函數返回值:Python 實現

来源:https://www.cnblogs.com/gscnblog/archive/2019/02/19/10403401.html
-Advertisement-
Play Games

對於經常調用的函數,特別是遞歸函數或計算密集的函數,記憶(緩存)返回值可以顯著提高性能。 ...


對於經常調用的函數,特別是遞歸函數或計算密集的函數,記憶(緩存)返回值可以顯著提高性能。而在 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({n})')
    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)
89
89

可以看到第二次直接輸出 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

除了這種手工製作的方法,有一個第三方庫 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({n})')
...     return x

以同樣的參數運行這個函數兩次,只有第一次會真正計算:

>>> print(f(1))
Running f(1)
1
>>> print(f(1))
1

參考

1 http://code.activestate.com/recipes/52201/

2 https://docs.python.org/3/tutorial/datastructures.html#dictionaries

3 https://joblib.readthedocs.io/en/latest/memory.html#use-case

(本文完)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 驗證框架 SpringBoot支持JSR-303,Bean等驗證框架 JSR-303 JSR-303是Java的標準驗證框架,已有實現Hibernate validator. JSR-303驗證類型 在MVC中使用JSR-303校驗 可以使用@Validated註解來觸發一次校驗 例子: index ...
  • ##### 1. 類型 ClassNotFoundException繼承自Exception,屬於java異常類。NoClassDefFoundError繼承自Error,在java中Error一般屬於不可恢復的系統錯誤,有JVM拋出,並且不能被開發者處理。 ##### 2. 產生的原因 Class ...
  • [TOC] 今天正月十五,可憐的我還在這裡碼字,首先祝大家“猿宵節”快樂!距離我發佈的 "spring cloud初級教程" 已經有段時間了,這段時間經歷了一個春節,加上年後我又有了點事情要做,所以我在初級教程中預告的spring cloud手腳架項目估計要食言了。不過今天冒個泡,就是讓大家知道,事 ...
  • 情景引入 很早之前,Java就火起來了,是因為它善於開發和處理網路方面的應用。 Java有一個愛好,就是喜歡制定規範標準,但自己又不善於去實現。 反倒是一些服務提供商使用它的規範標準來製造應用伺服器而賺的盆滿缽滿。 企業用戶因要使用這些應用伺服器而向提供商支付高額費用,而且也不是特別好用。 一個青年 ...
  • 1.在windows下打包 微服務應用通過maven進行打包,在項目的pom.xml執行mvn clean package,或者直接通過idea或者eclipse進行maven打包 之上操作將在項目的 target目錄生成文件microservice-discovery-eureka-0.0.1-S ...
  • 最近我們的項目在考慮使用Gateway,考慮使用Spring Cloud Gateway,發現網關的異常處理和spring boot 單體應用異常處理還是有很大區別的。讓我們來回顧一下異常。 ...
  • Semaphore用於管理信號量,在併發編程中,可以控制返訪問同步代碼的線程數量。Semaphore在實例化時傳入一個int值,也就是指明信號數量。主要方法有兩個:acquire()和release()。acquire()用於請求信號,每調用一次,信號量便少一個。release()用於釋放信號,調用 ...
  • 最近在學樹剖,看到了這題就做了 [ZJOI2008]樹的統計 思路 從題面可以知道,這題是樹剖題(要求的和模板沒什麼區別呀喂 就是在普通的樹剖上加了一個最大值 所以可以知道就是樹剖+特殊的線段樹 線段樹要可以求區間最大值和區間和 那麼就很好做了,基本上就是到樹剖模板題 只需要給線段樹加個最大值就行了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...