refer:
http://www.reibang.com/p/c5582e23b26c
https://my.oschina.net/leejun2005/blog/145911
不可變對(duì)象
- int, float, string, tuple
j = 2333
i = j
k = 2333
print id(i)
print id(j)
pring id(k)
print i is j
print j is k
print 'after j +1'
j = j + 1
print id(j)
print i
# output:
140565451698264
140565451698264
140565451698264
True
True
after j +1
2333
2334
140565451698216
- 不可變對(duì)象(2333)在內(nèi)存地址(140565451698264)中存放后,該值就不可變了亮曹;一旦去改變(j = j + 1)實(shí)際上是重新分配了內(nèi)存地址(140565451698216),新地址存的是所賦的值(j+1)橄杨,j 再指向新的內(nèi)存地址。
-
refer中的例子圖很形象:
image.png
可變對(duì)象
- list, dict, set
a = dict()
b = a
a["k"] = "v"
print "a id: {}".format(id(a))
print "b id: {}".format(id(b))
print b
# output:
a id: 4416436776
b id: 4416436776
{'k': 'v'}
可變對(duì)象a的地址是4416436776
,a賦值給b后照卦,實(shí)際是a和b指向同一個(gè)內(nèi)存地址式矫。
上述可見,改變a的值就是直接在該內(nèi)存空間直接改變存儲(chǔ)對(duì)象的值役耕,所以b的內(nèi)容也跟著變化了采转。
refer中的圖例:
函數(shù)的參數(shù)傳遞
python里的規(guī)則是函數(shù)的參數(shù)傳遞都是傳遞引用
,即傳入?yún)?shù)的內(nèi)存地址蹄葱。表面看氏义,函數(shù)內(nèi)部對(duì)參數(shù)的改變會(huì)影響參數(shù)本身。
但python有可變對(duì)象和不可變對(duì)象图云,結(jié)合上文可知惯悠,
- 參數(shù)是可變對(duì)象類型的時(shí)候,函數(shù)如果對(duì)參數(shù)有變動(dòng)會(huì)影響參數(shù)本身的值(“引用傳遞”)竣况,這個(gè)跟C的指針很像
- 參數(shù)是不可變對(duì)象類型的時(shí)候克婶,函數(shù)對(duì)參數(shù)的變動(dòng)實(shí)際上是重新分配了內(nèi)存空間的,所以參數(shù)本身值不受影響(值傳遞)
def test(a_int, b_list):
a_int = a_int + 1
b_list.append('13')
print('inner a_int:' + str(a_int))
print('inner b_list:' + str(b_list))
a_int = 5
b_list = [10, 11]
test(a_int, b_list)
print('outer a_int:' + str(a_int))
print('outer b_list:' + str(b_list))
# output:
inner a_int:6
inner b_list:[10, 11, '13']
outer a_int:5
outer b_list:[10, 11, '13']
示例講解
- 在 python 中賦值語句總是建立對(duì)象的引用值丹泉,而不是復(fù)制對(duì)象情萤。因此,python 變量更像是指針摹恨,而不是數(shù)據(jù)存儲(chǔ)區(qū)域筋岛,
lst = [0,1,2]
lst[1] = lst
lst
# output:
[0, [...], 2]
可以說 Python 沒有賦值,只有引用
晒哄。你這樣相當(dāng)于創(chuàng)建了一個(gè)引用自身的結(jié)構(gòu)睁宰,所以導(dǎo)致了無限循環(huán)
- 通過lst[:]形式來復(fù)制操作得到新對(duì)象
lst = [0,1,2]
lst[1] = lst[:]
lst
# output:
[0, [0,1,2], 2]
生成對(duì)象的拷貝或者是復(fù)制序列肪获,不再是引用和共享變量,但此法只能頂層復(fù)制
- 往更深處說柒傻,values[:] 復(fù)制操作是所謂的「淺復(fù)制」(shallow copy)
a = [0, [1, 2], 3]
b = a[:]
a[0] = 8
a[1][1] = 9
a # [8, [1, 9], 3]
b # [0, [1, 9], 3]
- 通過copy.deepcopy來【深復(fù)制】(實(shí)際上應(yīng)該是遞歸進(jìn)行copy)
import copy
a = [0, [1, 2], 3]
b = copy.deepcopy(a)
a[0] = 8
a[1][1] = 9
a # [8, [1, 9], 3]
b # [0, [1, 2], 3]
- 字典 copy 方法孝赫,D.copy() 能夠復(fù)制字典,但此法只能淺層復(fù)制
對(duì)可變對(duì)象處理+=需要注意
x = x + y红符,x 出現(xiàn)兩次青柄,必須執(zhí)行兩次,性能不好预侯,合并必須新建對(duì)象 x致开,然后復(fù)制兩個(gè)列表合并
屬于復(fù)制/拷貝
x += y,x 只出現(xiàn)一次萎馅,也只會(huì)計(jì)算一次喇喉,性能好,不生成新對(duì)象校坑,只在內(nèi)存塊末尾增加元素。
當(dāng) x千诬、y 為list時(shí)耍目, += 會(huì)自動(dòng)調(diào)用 extend 方法進(jìn)行合并運(yùn)算,in-place change徐绑。
屬于共享引用
L = [1, 2]
M = L
L = L + [3, 4]
print L, M
print "-------------------"
L = [1, 2]
M = L
L += [3, 4]
print L, M
[1, 2, 3, 4] [1, 2]
-------------------
[1, 2, 3, 4] [1, 2, 3, 4]
陷阱:使用可變的默認(rèn)參數(shù)
In[2]: def foo(a, b, c=[]):
... c.append(a)
... c.append(b)
... print(c)
...
In[3]: foo(1, 1)
[1, 1]
In[4]: foo(1, 1)
[1, 1, 1, 1]
In[5]: foo(1, 1)
[1, 1, 1, 1, 1, 1]
同一個(gè)變量c在函數(shù)調(diào)用的每一次都被反復(fù)引用邪驮。這可能有一些意想不到的后果。