當前位置:首頁 » 硬碟大全 » python會對實數進行緩存嗎
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

python會對實數進行緩存嗎

發布時間: 2023-01-22 16:01:51

㈠ 怎麼讓python在內存中運行

對象的內存使用
賦值語句是語言最常見的功能了。但即使是最簡單的賦值語句,也可以很有內涵。Python的賦值語句就很值得研究。
a = 1

整數1為一個對象。而a是一個引用。利用賦值語句,引用a指向對象1。Python是動態類型的語言(參考動態類型),對象與引用分離。Python像使用「筷子」那樣,通過引用來接觸和翻動真正的食物——對象。

引用和對象

為了探索對象在內存的存儲,我們可以求助於Python的內置函數id()。它用於返回對象的身份(identity)。其實,這里所謂的身份,就是該對象的內存地址。
a = 1

print(id(a))
print(hex(id(a)))

在我的計算機上,它們返回的是:
11246696
'0xab9c68'
分別為內存地址的十進制和十六進製表示。

在Python中,整數和短小的字元,Python都會緩存這些對象,以便重復使用。當我們創建多個等於1的引用時,實際上是讓所有這些引用指向同一個對象。
a = 1
b = 1

print(id(a))
print(id(b))

上面程序返回
11246696
11246696
可見a和b實際上是指向同一個對象的兩個引用。

為了檢驗兩個引用指向同一個對象,我們可以用is關鍵字。is用於判斷兩個引用所指的對象是否相同。

# True
a = 1
b = 1
print(a is b)

# True
a = "good"
b = "good"
print(a is b)

# False
a = "very good morning"
b = "very good morning"
print(a is b)

# False
a = []
b = []
print(a is b)

上面的注釋為相應的運行結果。可以看到,由於Python緩存了整數和短字元串,因此每個對象只存有一份。比如,所有整數1的引用都指向同一對象。即使使用賦值語句,也只是創造了新的引用,而不是對象本身。長的字元串和其它對象可以有多個相同的對象,可以使用賦值語句創建出新的對象。

在Python中,每個對象都有存有指向該對象的引用總數,即引用計數(reference count)。
我們可以使用sys包中的getrefcount(),來查看某個對象的引用計數。需要注意的是,當使用某個引用作為參數,傳遞給getrefcount()時,參數實際上創建了一個臨時的引用。因此,getrefcount()所得到的結果,會比期望的多1。

from sys import getrefcount

a = [1, 2, 3]
print(getrefcount(a))

b = a
print(getrefcount(b))

由於上述原因,兩個getrefcount將返回2和3,而不是期望的1和2。

對象引用對象
Python的一個容器對象(container),比如表、詞典等,可以包含多個對象。實際上,容器對象中包含的並不是元素對象本身,是指向各個元素對象的引用。
我們也可以自定義一個對象,並引用其它對象:

class from_obj(object):
def __init__(self, to_obj):
self.to_obj = to_obj

b = [1,2,3]
a = from_obj(b)
print(id(a.to_obj))
print(id(b))

可以看到,a引用了對象b。

對象引用對象,是Python最基本的構成方式。即使是a = 1這一賦值方式,實際上是讓詞典的一個鍵值"a"的元素引用整數對象1。該詞典對象用於記錄所有的全局引用。該詞典引用了整數對象1。我們可以通過內置函數globals()來查看該詞典。

當一個對象A被另一個對象B引用時,A的引用計數將增加1。

from sys import getrefcount

a = [1, 2, 3]
print(getrefcount(a))

b = [a, a]
print(getrefcount(a))

由於對象b引用了兩次a,a的引用計數增加了2。

容器對象的引用可能構成很復雜的拓撲結構。我們可以用objgraph包來繪制其引用關系,比如
x = [1, 2, 3]
y = [x, dict(key1=x)]
z = [y, (x, y)]

import objgraph
objgraph.show_refs([z], filename='ref_topo.png')

objgraph是Python的一個第三方包。安裝之前需要安裝xdot。
sudo apt-get install xdot
sudo pip install objgraph

objgraph官網

兩個對象可能相互引用,從而構成所謂的引用環(reference cycle)。
a = []
b = [a]
a.append(b)

即使是一個對象,只需要自己引用自己,也能構成引用環。
a = []
a.append(a)
print(getrefcount(a))

引用環會給垃圾回收機制帶來很大的麻煩,我將在後面詳細敘述這一點。

引用減少
某個對象的引用計數可能減少。比如,可以使用del關鍵字刪除某個引用:

from sys import getrefcount

a = [1, 2, 3]
b = a
print(getrefcount(b))

del a
print(getrefcount(b))

del也可以用於刪除容器元素中的元素,比如:

a = [1,2,3]
del a[0]
print(a)

如果某個引用指向對象A,當這個引用被重新定向到某個其他對象B時,對象A的引用計數減少:

from sys import getrefcount

a = [1, 2, 3]
b = a
print(getrefcount(b))

a = 1
print(getrefcount(b))

垃圾回收
吃太多,總會變胖,Python也是這樣。當Python中的對象越來越多,它們將占據越來越大的內存。不過你不用太擔心Python的體形,它會乖巧的在適當的時候「減肥」,啟動垃圾回收(garbage collection),將沒用的對象清除。在許多語言中都有垃圾回收機制,比如Java和Ruby。盡管最終目的都是塑造苗條的提醒,但不同語言的減肥方案有很大的差異 (這一點可以對比本文和Java內存管理與垃圾回收
)。

從基本原理上,當Python的某個對象的引用計數降為0時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如某個新建對象,它被分配給某個引用,對象的引用計數變為1。如果引用被刪除,對象的引用計數為0,那麼該對象就可以被垃圾回收。比如下面的表:
a = [1, 2, 3]
del a

del a後,已經沒有任何引用指向之前建立的[1, 2, 3]這個表。用戶不可能通過任何方式接觸或者動用這個對象。這個對象如果繼續待在內存里,就成了不健康的脂肪。當垃圾回收啟動時,Python掃描到這個引用計數為0的對象,就將它所佔據的內存清空。

然而,減肥是個昂貴而費力的事情。垃圾回收時,Python不能進行其它的任務。頻繁的垃圾回收將大大降低Python的工作效率。如果內存中的對象不多,就沒有必要總啟動垃圾回收。所以,Python只會在特定條件下,自動啟動垃圾回收。當Python運行時,會記錄其中分配對象(object allocation)和取消分配對象(object deallocation)的次數。當兩者的差值高於某個閾值時,垃圾回收才會啟動。
我們可以通過gc模塊的get_threshold()方法,查看該閾值:
import gc
print(gc.get_threshold())

返回(700, 10, 10),後面的兩個10是與分代回收相關的閾值,後面可以看到。700即是垃圾回收啟動的閾值。可以通過gc中的set_threshold()方法重新設置。

我們也可以手動啟動垃圾回收,即使用gc.collect()。

分代回收
Python同時採用了分代(generation)回收的策略。這一策略的基本假設是,存活時間越久的對象,越不可能在後面的程序中變成垃圾。我們的程序往往會產生大量的對象,許多對象很快產生和消失,但也有一些對象長期被使用。出於信任和效率,對於這樣一些「長壽」對象,我們相信它們的用處,所以減少在垃圾回收中掃描它們的頻率。

小傢伙要多檢查

Python將所有的對象分為0,1,2三代。所有的新建對象都是0代對象。當某一代對象經歷過垃圾回收,依然存活,那麼它就被歸入下一代對象。垃圾回收啟動時,一定會掃描所有的0代對象。如果0代經過一定次數垃圾回收,那麼就啟動對0代和1代的掃描清理。當1代也經歷了一定次數的垃圾回收後,那麼會啟動對0,1,2,即對所有對象進行掃描。
這兩個次數即上面get_threshold()返回的(700, 10, 10)返回的兩個10。也就是說,每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,才會有1次的2代垃圾回收。
同樣可以用set_threshold()來調整,比如對2代對象進行更頻繁的掃描。
import gc
gc.set_threshold(700, 10, 5)

孤立的引用環
引用環的存在會給上面的垃圾回收機制帶來很大的困難。這些引用環可能構成無法使用,但引用計數不為0的一些對象。
a = []
b = [a]
a.append(b)

del a
del b

上面我們先創建了兩個表對象,並引用對方,構成一個引用環。刪除了a,b引用之後,這兩個對象不可能再從程序中調用,就沒有什麼用處了。但是由於引用環的存在,這兩個對象的引用計數都沒有降到0,不會被垃圾回收。

孤立的引用環

為了回收這樣的引用環,Python復制每個對象的引用計數,可以記為gc_ref。假設,每個對象i,該計數為gc_ref_i。Python會遍歷所有的對象i。對於每個對象i引用的對象j,將相應的gc_ref_j減1。

遍歷後的結果

在結束遍歷後,gc_ref不為0的對象,和這些對象引用的對象,以及繼續更下游引用的對象,需要被保留。而其它的對象則被垃圾回收。

㈡ 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如何進行內存管理

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的行為來申請內存空間。

㈣ Python如何管理內存

Python中的內存管理是從三個方面來進行的,一對象的引用計數機制,二垃圾回收機制,三內存池機制
一、對象的引用計數機制
Python內部使用引用計數,來保持追蹤內存中的對象,所有對象都有引用計數。
引用計數增加的情況:
1,一個對象分配一個新名稱
2,將其放入一個容器中(如列表、元組或字典)
引用計數減少的情況:
1,使用del語句對對象別名顯示的銷毀
2,引用超出作用域或被重新賦值
sys.getrefcount( )函數可以獲得對象的當前引用計數
多數情況下,引用計數比你猜測得要大得多。對於不可變數據(如數字和字元串),解釋器會在程序的不同部分共享內存,以便節約內存。
二、垃圾回收
1,當一個對象的引用計數歸零時,它將被垃圾收集機制處理掉。
2,當兩個對象a和b相互引用時,del語句可以減少a和b的引用計數,並銷毀用於引用底層對象的名稱。然而由於每個對象都包含一個對其他對象的應用,因此引用計數不會歸零,對象也不會銷毀。(從而導致內存泄露)。為解決這一問題,解釋器會定期執行一個循環檢測器,搜索不可訪問對象的循環並刪除它們。
三、內存池機制
Python提供了對內存的垃圾收集機制,但是它將不用的內存放到內存池而不是返回給操作系統。
1,Pymalloc機制。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
2,Python中所有小於256個位元組的對象都使用pymalloc實現的分配器,而大的對象則使用系統的malloc。
3,對於Python對象,如整數,浮點數和List,都有其獨立的私有內存池,對象間不共享他們的內存池。也就是說如果你分配又釋放了大量的整數,用於緩存這些整數的內存就不能再分配給浮點數。

㈤ 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而言,存儲好的腳本文件(Script file)和在Console中的互動式(interactive)命令,執行方式不同。對於腳本文件,解釋器將其當作整個代碼塊執行,而對於交互性命令行中的每一條命令,解釋器將其當作單獨的代碼塊執行。而Python在執行同一個代碼塊的初始化對象的命令時,會檢查是否其值是否已經存在,如果存在,會將其重用(這句話不夠嚴謹,後面會詳談)。所以在你給出的例子中,文件執行時(同一個代碼塊)會把a、b兩個變數指向同一個對象;而在命令行執行時,a、b賦值語句分別被當作兩個代碼塊執行,所以會得到兩個不同的對象,因而is判斷返回False。

# 如果你能理解上面一段,就不用看下面的廢話了。

下面是詳細的回答:
說真的,這簡直是我最近在知乎遇到過的最好的問題!
這個問題遠超我想像中的復雜。我本來以為我能用兩分鍾搞定這種每日一水的問題,結果我花了一個小時搜來搜去,讀來讀去,還跑去群里跟人討論了一陣,都沒能找到答案。
大概兩個小時以後,我找到了相對正確的答案,把自己已經弄懂的部分強答一番,並邀請一些大神,希望能看到更為准確的回答。

這個問題的博大精深在於,能從中扯出許多小問題來,雖然這些東西很細枝末節,很trick,在日常編程中不怎麼用的到,更不怎麼需要額外關注,但是理解這些問題,對於我們理解Python的對象機制乃至內存處理機制有很大的幫助。

我從頭開始說,大概會分以下幾個部分來談,每個部分其實都能展開很廣,這次就把與問題相關的知識簡單一提:
(雖然我覺得按照我尋找答案的過程講,可能對認知更有幫助,但是理清頭緒的話可能更好理解,之後會找時間為這個問題寫篇文章好好記錄一下)

Python中的數據類型——可變與不可變
Python中is比較與==比較的區別
Python中對小整數的緩存機制
Python程序的結構——代碼塊
Python的內存管理——新建對象時的操作

聲明:以下所講機制,與Python不同版本的具體實現有關(implement specific)可能不同。

Python中的數據類型
Python中的數據類型,這可能是大家入門Python的第一節課。很簡單嘛,大家最常用的,int(包括long)、float、string、list、tuple、dict,加上bool和NoneType。
但是這里要重點說的,其實是可變類型和不可變類型。
不可變(immutable):Number(包括int、float),String,Tuple
可變(mutable):Dict,List,User-defined class
首先我們要記住一句話,一切皆對象。Python中把任何一種Type都當作對象來處理。其中有一些類型是不可變的,比如:

這個還是好理解的,在初始化賦值一個字元串後,我們沒有辦法直接修改它的值。但是數字呢?數字這種變來變去的又怎麼理解。
可以看出,a的值雖然從10變成了11,但是a這個變數指向內存中的位置發生了變化,也就是說我們並沒有對a指向的內存進行操作,而是對a進行了重新賦值。
再簡單舉一個可變的例子。

體會了可變與不可變的外在表現後,簡單理解一下為什麼不可變。
Python官方文檔這樣解釋字元串不可變:

There are several advantages.
One is performance: knowing that a string is immutable means we can allocate space for it at creation time, and the storage requirements are fixed and unchanging. This is also one of the reasons for the distinction between tuples and lists.
Another advantage is that strings in Python are considered as 「elemental」 as numbers. No amount of activity will change the value 8 to anything else, and in Python, no amount of activity will change the string 「eight」 to anything else.
個人感覺,有性能上的考慮(比如對一些固定不變的元素給予固定的存儲位置,整數這樣操作比較方便,字元串的話涉及一些比較也會減少後續操作的時間),也有一些安全上的考慮(比如列表中的值會改變,元組不會)。這個我也不太精通,就不展開談了。

Python中is比較與==比較的區別
前面已經提過一次,Python中一切皆對象。對象包含三個要素,id、type、value。
而Python中用於比較「相等」這一概念的操作符,is和==。
當兩個變數指向了同一個對象時,is會返回True(即is比較的是兩個變數的id);
當兩個變數的值相同時,==會返回True(即==比較的是兩個變數的value)。
示例(命令行交互模式下):
第一個和第三個示例是好理解的。
但是第二個就不那麼好理解了,尤其是配合下面這個(假定我們已經知道命令行中的語句執行是單獨執行兩次不會相互影響,後面會具體解釋):
為什麼a、b分別賦值1000時is比較返回False,可以分別賦值100就會返回True?

Python中對小整數的緩存機制
Python官方文檔中這么說:
The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-)簡單來說就是,Python自動將-5~256的整數進行了緩存,當你將這些整數賦值給變數時,並不會重新創建對象,而是使用已經創建好的緩存對象。

Python程序的結構——代碼塊&Python的內存管理——新建對象時的操作
終於要來到題主問題的部分了。
先來看最讓我們困惑的,也就是題主給出的示例吧(接下來用float演示,int是同樣的情況):
交互命令行下:
同樣的還有:
(說好的小整數才有緩存呢(摔)!這跟你講的不一樣啊教練!)
這就很尷尬了對吧。
其實從結果論出發,我們很容易猜到結論,就像題主自己也猜了個差不多——緩存機制不同。畢竟is比較的就是對象的id,也就是對象在內存中的位置,也就是是不是同一個對象。
既然腳本文件的執行結果是True,那麼,他倆就是同一個對象;既然命令行執行的結果是False,那麼他倆就不是同一個對象。(這他喵的不是廢話嗎!)
所以我開始了漫長的找原理的過程……然而網上這方面提及的實在太少。尤其是大家的大部分討論都是int的小整數緩存機制;就算討論到了float,也不實際解決我們的問題。

其實我都快要放棄了,漫無目的地翻stackoverflow推薦的相關問題時終於找到了一個類似的情況,但是人家並不是比較的腳本文件和命令行執行,而是比較的函數體和賦值語句:
同樣的代碼,拆開就是False,放函數里就是True!是不是很像我們遇到的情況了。
根據提示我們從官方文檔找到了這樣的說法:

A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a mole, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the 『-c『 option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.
A code block is executed in an execution frame. A frame contains some administrative information (used for debugging) and determines where and how execution continues after the code block』s execution has completed.
沒錯!跟我們猜的一樣!這就是原理的出處了!
代碼塊作為一個執行單元,一個模塊、一個函數體、一個類定義、一個腳本文件,都是一個代碼塊。
在互動式命令行中,每行代碼單獨視作一個代碼塊。

至此問題解決……了嗎?視作一個代碼塊,就意味著要把相同value的賦值指向相同的對象嗎?
在此重復一下'is' operator behaves unexpectedly with non-cached integers中提到的實驗,並簡單翻譯結論。
通過compile()函數和dis模塊的code_info()函數來檢測我們執行的命令的信息。
示例:
可以看出,分別賦值a,b得到的value相等,id是不一樣的。
把10.0 10.0 10.1分別賦值給a,b,c,可以看出結果中其實只保存了一個10.0,也就是a,b共用了這個數值。

也就是說,當命令行執行時,是以single的模式來compile代碼(2. Built-in Functions)。它會在u_consts字典中記錄對象常量。
The mode argument specifies what kind of code must be compiled; it can be 'exec' if source consists of a sequence of statements, 'eval' if it consists of a single expression, or 'single' if it consists of a single interactive statement (in the latter case, expression statements that evaluate to something other than None will be printed).而在同一代碼塊執行時,當增加新的常量,會先在字典中查詢記錄,所以相同賦值的變數會指向同一個對象而不是新建對象。

至此…問題大概是解決了。

㈦ python requests 會緩存,嗎

你好 python 並不會自動緩存數據, 極度懷疑你數據沒插入成功,或者插入操作不在那個2秒SLEEP的時間裡面。

㈧ 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. 分代回收
     從前面「標記-清除」這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。