簡體   English   中英

Python,變量存儲在內存中

[英]Python , variable store in memory

a=[1234,1234] #list

a      
[1234, 1234] 

id(a[0])      
38032480

id(a[1])      
38032480

b=1234 #b is a variable of integer type

id(b)      
38032384

為什么id(b)與python中的id(a [0])和id(a [1])不同?

當CPython REPL執行一行時,它將:

  1. 解析,並將其編譯為字節碼的代碼對象,然后
  2. 執行字節碼。

可以通過dis模塊檢查編譯結果:

>>> dis.dis('a = [1234, 1234, 5678, 90123, 5678, 4321]')
  1           0 LOAD_CONST               0 (1234)
              2 LOAD_CONST               0 (1234)
              4 LOAD_CONST               1 (5678)
              6 LOAD_CONST               2 (90123)
              8 LOAD_CONST               1 (5678)
             10 LOAD_CONST               3 (4321)
             12 BUILD_LIST               6
             14 STORE_NAME               0 (a)
             16 LOAD_CONST               4 (None)
             18 RETURN_VALUE

請注意,所有1234都加載了“ LOAD_CONST 0 ”,所有5678都加載了“ LOAD_CONST 1 ”。 這些引用與代碼對象關聯的常量表。 在這里,表格是(1234, 5678, 90123, 4321, None)

編譯器知道代碼對象中所有1234的副本都是相同的 ,因此只會為所有對象分配一個對象。

因此,正如OP所觀察到的, a[0]a[1]確實確實指向同一對象:該行代碼的代碼對象常量表中的同一常量。

當執行b = 1234 ,它將獨立於上一行再次被編譯和執行,因此將分配一個不同的對象。

(您可以閱讀http://akaptur.com/blog/categories/python-internals/以獲取有關如何解釋代碼對象的簡要介紹)


在REPL之外,當您執行*.py文件時,每個函數都會編譯成單獨的代碼對象,因此在運行時:

a = [1234, 1234]
b = 1234
print(id(a[0]), id(a[1]))
print(id(b))

a = (lambda: [1234, 1234])()
b = (lambda: 1234)()
print(id(a[0]), id(a[1]))
print(id(b))

我們可能會看到類似:

4415536880 4415536880
4415536880
4415536912 4415536912
4415537104
  • 前三個數字共享相同的地址4415536​​880,它們屬於“ __main__”代碼對象的常量
  • 然后, a[0]a[1]具有第一個lambda的地址4415536​​912。
  • b具有第二個lambda的地址4415537104。

另請注意,此結果僅對CPython有效。 其他實現在分配常量方面有不同的策略。 例如,在PyPy中運行上面的代碼將給出:

19745 19745
19745
19745 19745
19745

沒有規則或保證說明id(a [0])應該等於id(a [1]),因此問題本身是沒有根據的。 您應該問的問題是為什么id(a[0])id(a[1])實際上相同。
如果在a.append(1234)之后加上id(a[2]) ,則可能會或可能不會獲得相同的ID。 正如@hiro protagonist指出的那樣,這些只是內部優化,您不應依賴這些優化。

Python列表與C數組非常不同。

AC陣列只是一塊連續的內存,因此,根據定義,其第一個(第0個)元素的地址就是該陣列本身的地址。 C語言中的數組訪問只是指針算法, []符號只是該指針算法上語法糖的薄殼。 表達式int x[]只是int * x另一種形式。

為了便於說明,我們假設在Python中, id(x)是“ X的內存地址”,就像*x在C中一樣。(並非對所有Python實現都是如此,甚至不能保證在CPython。這只是一個唯一的數字。)

在C中,一個int只是與體系結構有關的字節數,因此,對於int x = 1 ,表達式*x指向這些字節。 Python中的所有內容都是一個對象, 包括數字 這就是為什么id(1)引用一個int類型的對象描述1 您可以調用其方法: (1).__str__()將返回字符串'1'

因此,當x = [1, 2, 3]id(x)是具有三個元素的list對象的“指針”。 list對象本身非常復雜。 但是x[0]不是組成整數1的字節; 它在內部是對數字1的int對象的引用 。因此, id(x[0])是該對象的“指針”。

用C術語來說,數組的元素可以看作是指向存儲在其中的對象的指針,而不是對象本身的指針。

由於沒有必要讓兩個對象代表相同的數字1,因此在Python解釋器運行期間id(1)始終相同。 插圖:

x = [1, 2, 3]
y = [1, 100, 1000]

assert id(x) != id(y)  # obviously
assert id(x[0]) == id(y[0]) == id(1) # yes, the same int object

CPython實際上為一些最常用的小數字預分配了對象( 請參閱此處的注釋 )。 對於較大的數字,事實並非如此,這可能導致兩個較大的“副本”具有不同的id()

您必須注意:id()實際上給出變量或文字值的id。 對於程序中使用的每個文字/值(即使在id()本身內),id()都會返回(試圖返回)程序生命周期內文字/變量的唯一標識符。 可用於:

  • 用戶:檢查兩個對象/變量是否與以下內容相同:a為b
  • Python:優化內存,即避免內存中相同內容的不必要重復

對於您的情況,即使a [0]和a [1]的值可以相同,也無法保證。 它取決於在python程序生命周期中由內部處理的文字/變量的創建順序/時間順序。

情況1:

Type "help", "copyright", "credits" or "license" for more information.
>>> a=[1234,1234] 
>>> id(a[0])
52687424
>>> id(a[1])
52687424

情況2(請注意,在情況末尾,a [0]和a [1]的值相同,但id不同):

Type "help", "copyright", "credits" or "license" for more information.
>>> a=[1,1234]
>>> id(1)
1776174736
>>> id(1234)
14611088
>>> id(a[0])
1776174736
>>> id(a[1])
14611008
>>> a[0]=1234
>>> id(1234)
14611104
>>> id(a[0])
14611152
>>> id(a[1])
14611008
>>>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM