Python傳值與傳址問(wèn)題
問(wèn)題:在Python中參數(shù)是如何傳遞的?
先說(shuō)結(jié)論:
python參數(shù)傳遞方式:傳遞對(duì)象引用(傳值與傳址的混合方式)
不可變對(duì)象(數(shù)字,字符串,元組):傳值
可變對(duì)象(字典,列表):傳址
1.傳值和傳址的區(qū)別
傳值就是傳入一個(gè)參數(shù)的值,函數(shù)里面如果對(duì)傳入?yún)?shù)重新賦值,函數(shù)的全局變量不會(huì)跟著改變
傳址就是傳入一個(gè)參數(shù)的地址,函數(shù)里面如果對(duì)傳入?yún)?shù)重新賦值,函數(shù)的全局變量也會(huì)跟著作出相應(yīng)改變
示例:
值傳遞:
def foo(arg):arg = 2print(arg)a = 1foo(a) # 輸出:2print(a) # 輸出:1
傳址:
def bar(args):args.append(1)b = []print(b)# 輸出:[]print(id(b)) # 輸出:4324106952bar(b)print(b) # 輸出:[1]print(id(b)) # 輸出:4324106952
分析:
Python函數(shù)中,參數(shù)傳遞本質(zhì)上是一種賦值操作,而賦值操作是名字到對(duì)象的一個(gè)綁定過(guò)程
代碼片段一:

a綁定了1,調(diào)用foo函數(shù)后,arg賦值1,當(dāng)arg執(zhí)行重新賦值為2之后,相當(dāng)于將1上的arg標(biāo)簽撕掉,貼到了2身上,并不影響a。因此print(a)的結(jié)果還是1。
代碼片段二:

一開(kāi)始b綁定空列表,調(diào)用函數(shù)時(shí)參數(shù)arg也綁定該空列表,當(dāng)函數(shù)執(zhí)行到append時(shí),并沒(méi)有重新賦值操作,append方法只是對(duì)列表插入一個(gè)元素,因?yàn)閎與arg都是綁定在同一個(gè)對(duì)象上,因此b的內(nèi)容在調(diào)用函數(shù)后也發(fā)生了變化。
2.copy與deepcopy
如果我們不想修改原始列表的內(nèi)容怎么辦呢?這時(shí)候就要用到copy函數(shù)了
代碼:
a = [1, 2, 3, 4, 5]b = a.copy()print(a)b[2] = 66print(a)print(b)print(id(a))print(id(b))#輸出[1, 2, 3, 4, 5][1, 2, 3, 4, 5][1, 2, 66, 4, 5]45078213764507821376
這里用了copy讓b與a相等,后面修改了b的值,并不影響a的值
但是copy只是淺拷貝,只拷貝一層的內(nèi)容,對(duì)于嵌套列表的情況并不能很好的實(shí)現(xiàn)我們的需求,比如下面這種情況
a = [1, [1, 2], 3]b = a.copy()a[1].append(4)print(a)print(b)# 輸出[1, [1, 2, 4], 3][1, [1, 2, 4], 3]
可以看到b的值也跟著一起改變了,這種結(jié)果顯然不是我們想要的
我們想完全復(fù)制列表,讓新列表與原列表完全沒(méi)有關(guān)系,這就需要用到我們的deepcopy,深拷貝
a = [1, [1, 2], 3]b = copy.deepcopy(a)a[1].append(4)print(a)print(b)# 輸出[1, [1, 2, 4], 3][1, [1, 2], 3]
3.總結(jié):
python參數(shù)傳遞方式:傳遞對(duì)象引用(傳值與傳址的混合方式)
不可變對(duì)象(數(shù)字,字符串,元組):傳值
可變對(duì)象(字典,列表):傳址
它們兩者的區(qū)別:
傳值就是傳入一個(gè)參數(shù)的值,
傳址就是傳入一個(gè)參數(shù)的地址,也就是內(nèi)存地址。
函數(shù)中對(duì)傳入對(duì)象重新賦值,傳值參數(shù)是不會(huì)改變的,用傳址傳入就會(huì)改變。
可以分情況使用copy與deepcopy避免修改原始對(duì)象。
以上!
博客地址:www.tangxuansite.com
