㈠ 如何在python中使用時間限制進行緩存
可以試試裝飾器
defcache(fn=None,time_to_live=3600*24):#oneDAYdefault(orwhatever)
ifnotfn:returnfunctools.partial(cache,time_to_live=time_to_live)
my_cache={}
def_inner_fn(*args,**kwargs)
kws=sorted(kwargs.items())#inpython3.6+youdontneedsorted
key=tuple(args)+tuple(kw)
ifkeynotinmy_cacheortime.time()>my_cache[key]['expires']:
my_cache[key]={"value":fn(*args,**kwargs),"expires":time.time()+time_to_live}
returnmy_cache[key]
return__inner_fn
@cache(time_to_live=3600)#anhour
defmy_sqrt(x):
returnx**0.5@cache(time_to_live=60*30)#30mins
defget_new_emails():
returnmy_stmp.get_email_count()
㈡ python 將計算結果保留到緩存中
定義一個延遲屬性的一種高效方法是通過使用一個描述器類,如下所示:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">class lazyproperty:
def init (self, func):
self.func = func
</pre>
你需要像下面這樣在一個類中使用它:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">import math
class Circle:
def init (self, radius):
self.radius = radius
</pre>
下面在一個交互環境中演示它的使用:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">>>> c = Circle(4.0)
</pre>
仔細觀察你會發現消息 Computing area 和 Computing perimeter 僅僅出現一次。
很多時候,構造一個延遲計算屬性的主要目的是為了提升性能。 例如,你可以避免計算這些屬性值,除非你真的需要它們。 這里演示的方案就是用來實現這樣的效果的, 只不過它是通過以非常高效的方式使用描述器的一個精妙特性來達到這種效果的。
正如在其他小節(如8.9小節)所講的那樣,當一個描述器被放入一個類的定義時, 每次訪問屬性時它的 __get__() 、 __set__() 和 __delete__() 方法就會被觸發。 不過,如果一個描述器僅僅只定義了一個 __get__() 方法的話,它比通常的具有更弱的綁定。 特別地,只有當被訪問屬性不在實例底層的字典中時 __get__() 方法才會被觸發。
lazyproperty 類利用這一點,使用 __get__() 方法在實例中存儲計算出來的值, 這個實例使用相同的名字作為它的property。 這樣一來,結果值被存儲在實例字典中並且以後就不需要再去計算這個property了。 你可以嘗試更深入的例子來觀察結果:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">>>> c = Circle(4.0)
</pre>
這種方案有一個小缺陷就是計算出的值被創建後是可以被修改的。例如:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">>>> c.area
Computing area
50.26548245743669
</pre>
如果你擔心這個問題,那麼可以使用一種稍微沒那麼高效的實現,就像下面這樣:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">def lazyproperty(func):
name = ' lazy ' + func. name
@property
def lazy(self):
if hasattr(self, name):
return getattr(self, name)
else:
value = func(self)
setattr(self, name, value)
return value
return lazy
</pre>
如果你使用這個版本,就會發現現在修改操作已經不被允許了:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4;">>>> c = Circle(4.0)
㈢ 軟體開發模版緩存具體步驟怎麼操作的呢
模板緩存就是把模版內容緩存到指定位置,只要涉及模版都可以設置緩存,
下面以py程序為例:
1.1 在導入搜索期間首先會被檢查的地方是 sys.moles。 這個映射起到緩存之前導入的所有模塊的作用(包括其中間路徑)。 因此如果之前導入過 foo.bar.baz,則 sys.moles 將包含 foo, foo.bar 和 foo.bar.baz 條目。 每個鍵的值就是相應的模塊對象。
在導入期間,會在 sys.moles 查找模塊名稱,如存在則其關聯的值就是需要導入的模塊,導入過程完成。 然而,如果值為 None,則會引發 MoleNotFoundError。 如果找不到指定模塊名稱,Python 將繼續搜索該模塊。
1.2 sys.moles 是可寫的。刪除鍵可能不會破壞關聯的模塊(因為其他模塊可能會保留對它的引用),但它會使命名模塊的緩存條目無效,導致 Python 在下次導入時重新搜索命名模塊。鍵也可以賦值為 None ,強制下一次導入模塊導致 MoleNotFoundError 。
但是要小心,因為如果你還保有對某個模塊對象的引用,同時停用其在 sys.moles 中的緩存條目,然後又再次導入該名稱的模塊,則前後兩個模塊對象將 不是 同一個。 相反地,
1.3 importlib.reload() 將重用 同一個 模塊對象,並簡單地通過重新運行模塊的代碼來重新初始化模塊內容。
1.1 在導入搜索期間首先會被檢查的地方是 sys.moles。 這個映射起到緩存之前導入的所有模塊的作用(包括其中間路徑)。 因此如果之前導入過 foo.bar.baz,則 sys.moles 將包含 foo, foo.bar 和 foo.bar.baz 條目。 每個鍵的值就是相應的模塊對象。
在導入期間,會在 sys.moles 查找模塊名稱,如存在則其關聯的值就是需要導入的模塊,導入過程完成。 然而,如果值為 None,則會引發 MoleNotFoundError。 如果找不到指定模塊名稱,Python 將繼續搜索該模塊。
1.2 sys.moles 是可寫的。刪除鍵可能不會破壞關聯的模塊(因為其他模塊可能會保留對它的引用),但它會使命名模塊的緩存條目無效,導致 Python 在下次導入時重新搜索命名模塊。鍵也可以賦值為 None ,強制下一次導入模塊導致 MoleNotFoundError 。
但是要小心,因為如果你還保有對某個模塊對象的引用,同時停用其在 sys.moles 中的緩存條目,然後又再次導入該名稱的模塊,則前後兩個模塊對象將 不是 同一個。 相反地,
1.3 importlib.reload() 將重用 同一個 模塊對象,並簡單地通過重新運行模塊的代碼來重新初始化模塊內容。
㈣ Python 的內存管理機制
Python採用自動內存管理,即Python會自動進行垃圾回收,不需要像C、C++語言一樣需要程序員手動釋放內存,手動釋放可以做到實時性,但是存在內存泄露、空指針等風險。
Python自動垃圾回收也有自己的優點和缺點:優點:
缺點:
Python的垃圾回收機制採用 以引用計數法為主,分代回收為輔 的策略。
先聊引用計數法,Python中每個對象都有一個核心的結構體,如下
一個對象被創建時,引用計數值為1,當一個變數引用一個對象時,該對象的引用計數ob_refcnt就加一,當一個變數不再引用一個對象時,該對象的引用計數ob_refcnt就減一,Python判斷是否回收一個對象,會將該對象的引用計數值ob_refcnt減一判斷結果是否等於0,如果等於0就回收,如果不等於0就不回收,如下:
一個對象在以下三種情況下引用計數會增加:
一個對象在以下三種情況引用計數會減少:
驗證案例:
運行結果:
事實上,關於垃圾回收的測試,最好在終端環境下測試,比如整數257,它在PyCharm中用下面的測試代碼列印出來的結果是4,而如果在終端環境下列印出來的結果是2。這是因為終端代表的是原始的Python環境,而PyCharm等IDE做了一些特殊處理,在Python原始環境中,整數緩存的范圍是在 [-5, 256] 的雙閉合區間內,而PyCharm做了特殊處理之後,PyCharm整數緩存的范圍變成了 [-5, 無窮大],但我們必須以終端的測試結果為主,因為它代表的是原始的Python環境,並且代碼最終也都是要發布到終端運行的。
好,那麼回到終端,我們來看兩種特殊情況
前面學習過了,整數緩存的范圍是在 [-5, 256] 之間,這些整數對象在程序載入完全就已經駐留在內存之中,並且直到程序結束退出才會釋放佔有的內存,測試案例如下:
如果字元串的內容只由字母、數字、下劃線構成,那麼它只會創建一個對象駐留在內存中,否則,每創建一次都是一個新的對象。
引用計數法有缺陷,它無法解決循環引用問題,即A對象引用了B對象,B對象又引用了A對象,這種情況下,A、B兩個對象都無法通過引用計數法來進行回收,有一種解決方法是程序運行結束退出時進行回收,代碼如下:
前面講過,Python垃圾回收機制的策略是 以引用計數法為主,以分代回收為輔 。分代回收就是為了解決循環引用問題的。
Python採用分代來管理對象的生命周期:第0代、第1代、第2代,當一個對象被創建時,會被分配到第一代,默認情況下,當第0代的對象達到700個時,就會對處於第0代的對象進行檢測和回收,將存在循環引用的對象釋放內存,經過垃圾回收後,第0代中存活的對象會被分配為第1代,同樣,當第1代的對象個數達到10個時,也會對第1代的對象進行檢測和回收,將存在循環引用的對象釋放內存,經過垃圾回收後,第1代中存活的對象會被分配為第2代,同樣,當第二代的對象個數達到10個時,也會對第2代的對象進行檢測和回收,將存在循環引用的對象釋放內存。Python就是通過這樣一種策略來解決對象之間的循環引用問題的。
測試案例:
運行結果:
如上面的運行結果,當第一代中對象的個數達到699個即將突破臨界值700時(在列印699之前就已經回收了,所以看不到698和699)進行了垃圾回收,回收掉了循環引用的對象。
第一代、第二代、第三代分代回收都是有臨界值的,這個臨界值可以通過調用 gc.get_threshold 方法查看,如下:
當然,如果對默認臨界值不滿意,也可以調用 gc.set_threshold 方法來自定義臨界值,如下:
最後,簡單列出兩個gc的其它方法,了解一下,但禁止在程序代碼中使用
以上就是對Python垃圾回收的簡單介紹,當然,深入研究肯定不止這些內容,目前,了解到這個程度也足夠了。
㈤ python的內存管理機制
論壇
活動
招聘
專題
打開CSDN APP
Copyright © 1999-2020, CSDN.NET, All Rights Reserved
登錄
XCCS_澍
關注
Python 的內存管理機制及調優手段? 原創
2018-08-05 06:50:53
XCCS_澍
碼齡7年
關注
內存管理機制:引用計數、垃圾回收、內存池。
一、引用計數:
引用計數是一種非常高效的內存管理手段, 當一個 Python 對象被引用時其引用計數增加 1, 當其不再被一個變數引用時則計數減 1. 當引用計數等於 0 時對象被刪除。
二、垃圾回收 :
1. 引用計數
引用計數也是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術。當 Python 的某個對象的引用計數降為 0 時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如某個新建對象,它被分配給某個引用,對象的引用計數變為 1。如果引用被刪除,對象的引用計數為 0,那麼該對象就可以被垃圾回收。不過如果出現循環引用的話,引用計數機制就不再起有效的作用了
2. 標記清除
如果兩個對象的引用計數都為 1,但是僅僅存在他們之間的循環引用,那麼這兩個對象都是需要被回收的,也就是說,它們的引用計數雖然表現為非 0,但實際上有效的引用計數為 0。所以先將循環引用摘掉,就會得出這兩個對象的有效計數。
3. 分代回收
從前面「標記-清除」這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。
㈥ Python內存駐留機制
字元串駐留機制在許多面向對象編程語言中都支持,比如Java、python、Ruby、PHP等,它是一種數據緩存機制,對不可變數據類型使用同一個內存地址,有效的節省了空間,本文主要介紹Python的內存駐留機制。
字元串駐留就是每個字元串只有一個副本,多個對象共享該副本,駐留只針對不可變數據類型,比如字元串,布爾值,數字等。在這些固定數據類型處理中,使用駐留可以有效節省時間和空間,當然在駐留池中創建或者插入新的內容會消耗一定的時間。
下面舉例介紹python中的駐留機制。
在Python對象及內存管理機制一文中介紹了python的參數傳遞以及以及內存管理機制,來看下面一段代碼:
知道結果是什麼嗎?下面是執行結果:
l1和l2內容相同,卻指向了不同的內存地址,l2和l3之間使用等號賦值,所以指向了同一個對象。因為列表是可變對象,每創建一個列表,都會重新分配內存,列表對象是沒有「內存駐留」機制的。下面來看不可變數據類型的駐留機制。
在 Jupyter或者控制台交互環境 中執行下面代碼:
執行結果:
可以發現a1和b1指向了不同的地址,a2和b2指向了相同的地址,這是為什麼呢?
因為啟動時,Python 將一個 -5~256 之間整數列表預載入(緩存)到內存中,我們在這個范圍內創建一個整數對象時,python會自動引用緩存的對象,不會創建新的整數對象。
浮點型不支持:
如果上面的代碼在非交互環境,也就是將代碼作為python腳本運行的結果是什麼呢?(運行環境為python3.7)
全為True,沒有明確的限定臨界值,都進行了駐留操作。這是因為使用不同的環境時,代碼的優化方式不同。
在 Jupyter或者控制台交互環境 中:
滿足標識符命名規范的字元:
結果:
乘法獲取字元串(運行環境為python3.7)
結果:
在非交互環境中:
注意: 字元串是在編譯時進行駐留 ,也就是說,如果字元串的值不能在編譯時進行計算,將不會駐留。比如下面的例子:
在交互環境執行結果如下:
都指向不同的內存。
python 3.7 非交互環境執行結果:
發現d和e指向不同的內存,因為d和e不是在編譯時計算的,而是在運行時計算的。前面的 a = 'aa'*50 是在編譯時計算的。
除了上面介紹的python默認的駐留外,可以使用sys模塊中的intern()函數來指定駐留內容
結果:
使用intern()後,都指向了相同的地址。
本文主要介紹了python的內存駐留,內存駐留是python優化的一種策略,注意不同運行環境下優化策略不一樣,不同的python版本也不相同。注意字元串是在編譯時進行駐留。
--THE END--
㈦ python如何進行內存管理
Python的內存管理主要有三種機制:引用計數機制,垃圾回收機制和內存池機制。
引用計數機制
簡介
python內部使用引用計數,來保持追蹤內存中的對象,Python內部記錄了對象有多少個引用,即引用計數,當對象被創建時就創建了一個引用計數,當對象不再需要時,這個對象的引用計數為0時,它被垃圾回收。
特性
1.當給一個對象分配一個新名稱或者將一個對象放入一個容器(列表、元組或字典)時,該對象的引用計數都會增加。
2.當使用del對對象顯示銷毀或者引用超出作用於或者被重新賦值時,該對象的引用計數就會減少。
3.可以使用sys.getrefcount()函數來獲取對象的當前引用計數。多數情況下,引用計數要比我們猜測的大的多。對於不可變數據(數字和字元串),解釋器會在程序的不同部分共享內存,以便節約內存。
垃圾回收機制
特性
1.當內存中有不再使用的部分時,垃圾收集器就會把他們清理掉。它會去檢查那些引用計數為0的對象,然後清除其在內存的空間。當然除了引用計數為0的會被清除,還有一種情況也會被垃圾收集器清掉:當兩個對象相互引用時,他們本身其他的引用已經為0了。
2.垃圾回收機制還有一個循環垃圾回收器, 確保釋放循環引用對象(a引用b, b引用a, 導致其引用計數永遠不為0)。
內存池機制
簡介
在Python中,許多時候申請的內存都是小塊的內存,這些小塊內存在申請後,很快又會被釋放,由於這些內存的申請並不是為了創建對象,所以並沒有對象一級的內存池機制。這就意味著Python在運行期間會大量地執行malloc和free的操作,頻繁地在用戶態和核心態之間進行切換,這將嚴重影響Python的執行效率。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
內存池概念
內存池的概念就是預先在內存中申請一定數量的,大小相等的內存塊留作備用,當有新的內存需求時,就先從內存池中分配內存給這個需求,不夠了之後再申請新的內存。這樣做最顯著的優勢就是能夠減少內存碎片,提升效率。內存池的實現方式有很多,性能和適用范圍也不一樣。
特性
1.Python提供了對內存的垃圾收集機制,但是它將不用的內存放到內存池而不是返回給操作系統。
2.Pymalloc機制。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
3.Python中所有小於256個位元組的對象都使用pymalloc實現的分配器,而大的對象則使用系統的 malloc。
4.對於Python對象,如整數,浮點數和List,都有其獨立的私有內存池,對象間不共享他們的內存池。也就是說如果你分配又釋放了大量的整數,用於緩存這些整數的內存就不能再分配給浮點數。
㈧ python 如何釋放緩存
我覺得可能是因為你的py文件在第一次啟動後,已經編譯成pyc文件了,再次啟動的時候都是載入pyc,省去了編譯的階段,所以速度很快。
你可以試著把程序目錄下的所有pyc或者你的代碼文件對應的pyc文件刪除,看看是不是可以和第一次載入速度相同
㈨ Python性能提升神器!lru_cache的介紹和講解
我們經常談論的緩存一詞,更多的類似於將硬碟中的數據存放到內存中以至於提高讀取速度,比如常說的redis,就經常用來做數據的緩存。 Python的緩存(lru_cache)是一種裝飾在被執行的函數上,將其執行的結果緩存起來,當下次請求的時候,如果請求該函數的傳參未變則直接返回緩存起來的結果而不再執行函數的一種緩存裝飾器。
那它和redis的區別在哪?有什麼優勢?怎麼使用? 下面為你講解
1.現在我們先不使用緩存來寫一個求兩數之和的函數,並調用執行它兩次:
執行結果
可以看到 test 被執行了兩次,現在我們加上緩存再進行執行:
執行結果
可以看到 test 函數只被執行了一次,第二次的調用直接輸出了結果,使用了緩存起來的值。
2.當我們使用遞歸求斐波拉契數列 (斐波那契數列指的是這樣一個數列:0,1,1,2,3,5,8,它從第3項開始,每一項都等於前兩項之和) 的時候,緩存對性能的提升就尤其明顯了:
不使用緩存求第40項的斐波拉契數列
執行時間
使用緩存求第40項的斐波拉契數列:
執行時間
兩個差距是非常明顯的,因為不使用緩存時,相當於要重復執行了很多的函數,而使用了 lru_cache 則把之前執行的函數結果已經緩存了起來,就不需要再次執行了。
查看lru_cache源碼會發現它可以傳遞兩個參數: maxsize 、 typed :
代表被lru_cache裝飾的方法最大可緩存的結果數量 (被裝飾方法傳參不同一樣,則結果不一樣;如果傳參一樣則為同一個結果) , 如果不指定傳參則默認值為128,表示最多緩存128個返回結果,當達到了128個時,有新的結果要保存時,則會刪除最舊的那個結果。如果maxsize傳入為None則表示可以緩存無限個結果;
默認為false,代表不區分數據類型,如果設置為True,則會區分傳參類型進行緩存,官方是這樣描述的:
但在python3.9.8版本下進行測試,typed為false時,按照官方的測試方法測試得到的還是會被當成不同的結果處理,這個時候typed為false還是為true都會區別緩存,這與官方文檔的描述存在差異:
執行結果
但如果是多參數的情況下,則會被當成一個結果:
執行結果
這個時候設置typed為true時,則會區別緩存:
執行結果
當傳參個數大於1時,才符合官方的說法,不清楚是不是官方舉例有誤
當傳遞的參數是dict、list等的可變參數時,lru_cache是不支持的,會報錯:
報錯結果
緩存 緩存位置 是否支持可變參數 是否支持分布式 是否支持過期時間設置 支持的數據結構 需單獨安裝 redis 緩存在redis管理的內存中 是 是 是 支持5種數據結構 是 lru_cache 緩存在應用進程的內存中,應用被關閉則被清空 否 否 否 字典(參數為:key,結果為:value) 否
經過上面的分析,lru_cache 功能相對於redis來說要簡單許多,但使用起來更加方便,適用於小型的單體應用。如果涉及的緩存的數據種類比較多並且想更好的管理緩存、或者需要緩存數據有過期時間(類似登錄驗證的token)等,使用redis是優於lru_cache的。
㈩ Python如何進行內存管理
Python的內存管理,一般從以下三個方面來說:
1)對象的引用計數機制(四增五減)
2)垃圾回收機制(手動自動,分代回收)
3)內存池機制(大m小p)
1)對象的引用計數機制
要保持追蹤內存中的對象,Python使用了引用計數這一簡單的技術。sys.getrefcount(a)可以查看a對象的引用計數,但是比正常計數大1,因為調用函數的時候傳入a,這會讓a的引用計數+1
2)垃圾回收機制
吃太多,總會變胖,Python也是這樣。當Python中的對象越來越多,它們將占據越來越大的內存。不過你不用太擔心Python的體形,它會在適當的時候「減肥」,啟動垃圾回收(garbage
collection),將沒用的對象清除
從基本原理上,當Python的某個對象的引用計數降為0時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了
比如某個新建對象,它被分配給某個引用,對象的引用計數變為1。如果引用被刪除,對象的引用計數為0,那麼該對象就可以被垃圾回收。
然而,減肥是個昂貴而費力的事情。垃圾回收時,Python不能進行其它的任務。頻繁的垃圾回收將大大降低Python的工作效率。如果內存中的對象不多,就沒有必要總啟動垃圾回收。
所以,Python只會在特定條件下,自動啟動垃圾回收。當Python運行時,會記錄其中分配對象(object
allocation)和取消分配對象(object deallocation)的次數。當兩者的差值高於某個閾值時,垃圾回收才會啟動。
我們可以通過gc模塊的get_threshold()方法,查看該閾值。
3)內存池機制
Python中有分為大內存和小內存:(256K為界限分大小內存)
1、大內存使用malloc進行分配
2、小內存使用內存池進行分配
python中的內存管理機制都有兩套實現,一套是針對小對象,就是大小小於256K時,pymalloc會在內存池中申請內存空間;當大於256K時,則會直接執行系統的malloc的行為來申請內存空間。