Python對象內存地址

    在python中,萬物皆對象,常見的整數、浮點數、字符串、元祖、列表等類型,以及各種class、class instance等等都是對象。這些對象在python解釋器內部的地址是怎樣的呢?這裏我們只簡單看下python對象內存地址的相關基礎知識,以及編碼過程中一些注意事項,關於python解釋器的內存管理機制,涉及到解釋器內核的內存池原理,這裏不做深入探討,有興趣的朋友可以去閱讀解釋器源代碼。

0x01 不可變對象

    不可變對象是指對象的內存值不能被改變。Python中變量以引用的方式指向對象,如果變量引用了不可變對象,當改變該變量時,由於其所指的對象的值不能被改變,相當於把原來的值複製一份後再改變,這會開闢一個新的地址,變量再指向這個新的地址,即變量引用了新的對象。

    數值類型(整數和浮點)、字符串str、元組tuple都是不可變類型。比如a=1,b=[1],c={'a':1},id(a)、id(b[0])、id(1)、id(c['a'])將輸出一樣的值,因爲1是不可變對象,其在內存中是不可改變的。

0x02 可變對象

    可變對象是指對象的內存值可以被改變,變量(準確的說是引用)改變後,實際上是其所指的值直接發生改變,並沒有發生複製行爲,也沒有開闢新的內存地址,通俗點說就是原地改變。列表list、字典dict、集合set是可變類型。

0x03 對象的內存地址

    可以使用內置函數id()查看python對象的內存地址。下面是一些注意事項:

    (1) python中所有數字、字符串、list等值,創建時會分配內存空間,變量通過引用的方式使用它們。比如a=1和b=1,id(a)和id(b)的輸出一樣,表示a和b都指向相同的內存地址,即引用了同一個不可變對象;但是a=[1]和b=[1],id(a)和id(b)將輸出不一樣的值,a和b指向的是不同的內存地址,即引用了不同的可變對象,說明各可變對象是相互獨立的,在內存中有獨立的內存地址

    (2) 可用 is 判斷兩個對象的id(即內存地址)是否一樣,用 == 判斷兩個對象的值是否一樣。None值也有內存地址

    (3) list、set對象有各自的獨立內存空間,他們的各元素以引用的方式指向可變、不可變對象;

    (4) 函數形參的默認值,在內存中會開闢獨立的內存空間。比如測試代碼中test函數的param參數,其默認值是空list,如果調用時未傳參,則param指向內存中預先分配好的地址,該地址存儲的是list類型的值;當調用時傳參爲a,則param引用了a指向的內存空間;

    (5) python使用引用計數和垃圾回收來釋放內存對象,每個內存對象都維護了一個引用計數包括各種數字、字符串、list、set等類型值,以及類實例對象等等,當這些對象的引用計數爲 0 時,會被解釋器回收內存。每次對對象進行引用操作,都會導致其引用計數加1, 如下面測試代碼中的整數1,列表a、b、c、d、n都引用了整數1,以及test函數中的append操作,都會導致數字1的引用計數加1

    (6) copy和deepcopy方法都創建了新的內存對象,如測試代碼中的b和c都是新的變量,其各個元素可能是指向同一個內存空間。賦值操作是指向同一個內存塊,同時增加引用計數。copy是淺拷貝,deepcopy是深拷貝,特別對於可變對象,copy是以引用的方式指向同一個可變對象,而deepcopy會開闢新的內存地址,也就是創建了新的可變對象。

0x04 測試代碼

# -*- coding: utf8 -*-
import copy
import sys

a = [1, 2, [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)
d = a

print 'address of a:', id(a)
print 'address of b:', id(b)
print 'address of c:', id(c)
print 'address of d:', id(d)
print 'address of 1:', id(1)
print 'address of element 0 in a:', id(a[0])
print 'address of element 0 in b:', id(b[0])
print 'address of element 0 in c:', id(c[0])
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

a[0] = 99
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

print 'address of element 0 in a:', id(a[0])
print 'address of element 0 in b:', id(b[0])
print 'address of element 0 in c:', id(c[0])

print 'address of element 2 in a:', id(a[2])
print 'address of element 2 in b:', id(b[2])
print 'address of element 2 in c:', id(c[2])

a[2].append(5)
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

def test(param=[]):
    print 'address of param:', id(param)
    param.append(1)
    print 'reference count of 1:', sys.getrefcount(1)
    return param

print test(a)
print test()
print test()
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

print 'reference count of 1:', sys.getrefcount(1)
n = 1
print 'reference count of 1:', sys.getrefcount(1)
del n
print 'reference count of 1:', sys.getrefcount(1)

0x06 運行結果

address of a: 54681224
address of b: 54716296
address of c: 54692104
address of d: 54681224
address of 1: 48258856
address of element 0 in a: 48258856
address of element 0 in b: 48258856
address of element 0 in c: 48258856
a= [1, 2, [3, 4]]
b= [1, 2, [3, 4]]
c= [1, 2, [3, 4]]
d= [1, 2, [3, 4]]
a= [99, 2, [3, 4]]
b= [1, 2, [3, 4]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4]]
address of element 0 in a: 48260488
address of element 0 in b: 48258856
address of element 0 in c: 48258856
address of element 2 in a: 54692232
address of element 2 in b: 54692232
address of element 2 in c: 54716360
a= [99, 2, [3, 4, 5]]
b= [1, 2, [3, 4, 5]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4, 5]]
address of param: 54681224
reference count of 1: 161
[99, 2, [3, 4, 5], 1]
address of param: 54716424
reference count of 1: 162
[1]
address of param: 54716424
reference count of 1: 163
[1, 1]
a= [99, 2, [3, 4, 5], 1]
b= [1, 2, [3, 4, 5]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4, 5], 1]
reference count of 1: 163
reference count of 1: 164
reference count of 1: 163

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章