1. 程式人生 > >Python中的不可變物件和可變物件、Python對於不可變物件列表和可變物件列表做列表乘法

Python中的不可變物件和可變物件、Python對於不可變物件列表和可變物件列表做列表乘法

Python中的不可變物件和可變物件:

不可變物件指該物件所指向的記憶體中的值不能被改變。當改變某個變數時候,由於其所指的值不能被改變,相當於把原來的值複製一份後再改變,這會開闢一個新的地址,變數再指向這個新的地址。

可變物件指該物件所指向的記憶體中的值可以被改變。變數(準確的說是引用)改變後,實際上是其所指的值直接發生改變,並沒有發生複製行為,也沒有開闢新的出地址,通俗點說就是原地改變

在Python中,數值型別(intfloat)、字串str、元組tuple都是不可變型別。而列表list、字典dict、集合set是可變型別。

對於不可變物件

不可變物件的值改變前後的記憶體地址變化:

is 是判斷兩個物件的id是否相同, 而 == 判斷的是內容是否相同。

如果是int或float型數值:

a = 2
b = a
c = 2

print(id(a), id(b), id(c))
print(a is b)
print(a == b)
print(c is b)
c = a + 1
print(id(a), id(b), id(c))
print(c is b)

執行結果如下:

503147568 503147568 503147568
True
True
True
503147568 503147568 503147600
False

Process finished with exit code 0

如果是字串:

a_str = 'good'
b_str = 'good'
c_str = a_str + ''
print(a_str is b_str)
print(c_str is b_str)
print(id(a_str), id(b_str), id(c_str))
a_str = a_str + 'aa'
print(id(a_str), id(b_str), id(c_str))

執行結果如下:

True
True
37597624 37597624 37597624
42819064 37597624 37597624

Process finished with exit code 0

不可變物件變數對應記憶體的值不允許被改變。因此不可變物件的變數值要改變時,實際上是把原來的值複製一份後再改變,開闢一個新的記憶體地址,astr再指向這個新的記憶體地址,所以改變前後的id不一樣,原來astr對應的值因為不再有物件指向它,就會被垃圾回收。

特殊情況:元組(tuple)

add = (1, 2, 3)
aee = (1, 2, 3)
print(id(add), id(aee), id((1, 2, 3)))
# 加空元組
aee += ()
print(add, aee)
print(id(add), id(aee), id((1, 2, 3)))
aff = add
print(id(add), id(aff))
aff += (4, 5, 6)
print(id(add), id(aff))
print(add, aff)

執行結果如下:

42887640 42976744 42921416
(1, 2, 3) (1, 2, 3)
42887640 43022232 43021656
42887640 42887640
42887640 42979592
(1, 2, 3) (1, 2, 3, 4, 5, 6)

Process finished with exit code 0

add和aee都是(1,2,3),但是id卻不一樣。因為我們是單獨給add和aee都定義為(1,2,3)。當我們給aee加上一個空元組後,aee的id又變化了。aff=add,所以aff和add的id和內容就都是一樣的。當我們修改了aff時,aff的id變化了。

tuple是不可變物件,但又和str和數值型別稍微有點區別。平常說的tuple不可變更多時候是指裡面存放的值不能被改變(有些特殊情況,如tuple裡面存放了list,可改變list裡的元素。但實際上這個tuple並沒有被改變)。

對於str、int、float,只要變數的型別和值都相同,那麼變數的id也相同。

可變物件的值改變前後的記憶體地址變化:

如果是列表:

l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 is l2)
print(id(l1), id(l2), id([1, 2, 3]))
l3 = l2
print(id(l1), id(l2), id(l3))
l3.append(4)
print(l2, l3)
print(id(l1), id(l2), id(l3))

執行結果如下:

False
40303368 40303496 40941704
40303368 40303496 40303496
[1, 2, 3, 4] [1, 2, 3, 4]
40303368 40303496 40303496

Process finished with exit code 0

l3=l2,那麼l3世紀上是對l2的引用,我們可以看到l3和l2的id一樣。l1和l2的內容一樣,但id卻不一樣。

當我們改變l3時,由於l3是對l2的引用,故l2的內容也改變了。

如果是set:

abb = {1, 2, 3}
acc = abb
print(id(abb), id(acc))
acc.add(4)
print(abb, acc)
print(id(abb), id(acc))

執行結果如下:

43093128 43093128
{1, 2, 3, 4} {1, 2, 3, 4}
43093128 43093128

Process finished with exit code 0

acc是對abb的引用,所以改變acc的內容,abb的內容也改變了。

注意:

如果是拷貝函式,就僅僅是將內容拷貝過去,傳遞的並不是引用。這在想使用列表的值又不想修改原列表的時候特別有用。

如:

l1 = [1, 2, 3]
l2 = l1.copy()
print(l1, l2)
print(id(l1), id(l2))
l2.append(4)
print(l1, l2)
print(id(l1), id(l2))

執行結果如下:

[1, 2, 3] [1, 2, 3]
42072840 42072968
[1, 2, 3] [1, 2, 3, 4]
42072840 42072968

Process finished with exit code 0

此時修改l2,l1的內容不變。

作為函式引數時,可變型別傳遞的是引用,不可變型別傳遞的是內容:

如:

l1 = [1, 2, 3, 4]
str1 = 'HELLO'


def listchange(list_a):
	list_a.append(5)
	return list_a


def strchange(astr):
	astr = astr.lower()
	return astr


l_c = listchange(l1)
str_c = strchange(str1)
print(l1, l_c)
print(id(l1), id(l_c))
print(str1, str_c)
print(id(str1), id(str_c))

執行結果如下:

[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
40172296 40172296
HELLO hello
37728696 42884040

Process finished with exit code 0

list是可變型別,傳遞進函式的是引用,因此修改list_a內容後l1的內容也變化了;str是不可變型別,傳遞僅函式的是內容,修改astr後str1的內容不變。

Python對於不可變物件列表和可變物件列表做列表乘法:

如:

a = [1]
b = a * 4
print(a, b)
print(id(a), id(b[0]), id(b[1]), id(b[2]), id(b[3]))
a[0] = 2
print(a, b)
print(id(a), id(b[0]), id(b[1]), id(b[2]), id(b[3]))

c = [{'key': 1}]
d = c * 4
print(c, d)
print(id(c), id(d[0]), id(d[1]), id(d[2]), id(d[3]))
d[0]['key'] = 2
print(c, d)
print(id(c), id(d[0]), id(d[1]), id(d[2]), id(d[3]))

執行結果如下:

[1] [1, 1, 1, 1]
42138376 500329488 500329488 500329488 500329488
[2] [1, 1, 1, 1]
42138376 500329488 500329488 500329488 500329488
[{'key': 1}] [{'key': 1}, {'key': 1}, {'key': 1}, {'key': 1}]
42776712 38170200 38170200 38170200 38170200
[{'key': 2}] [{'key': 2}, {'key': 2}, {'key': 2}, {'key': 2}]
42776712 38170200 38170200 38170200 38170200

Process finished with exit code 0

a中的元素1是int型,是不可變物件,做列表乘法後,b中的四個1都是同一個新地址,修改a[0]後,b不受影響。

c中的元素是一個字典,是可變物件,做列表乘法後,d中的4個元素都是同一個新地址,修改c[0]後,d中的4個元素的值也一起改變了。這說明d中的4個元素都是c中字典物件元素的引用。