python調(diào)用的函數(shù)哆姻,傳遞參數(shù)的時候芙沥,是傳值還是傳遞引用优俘?
對于一段這樣的代碼京办,在main函數(shù)里面創(chuàng)建一個對象val=3,然后在test_function 里面把它修改成300帆焕,然后在main函數(shù)里面輸出惭婿,它的值應(yīng)該是3還是300?
答案:3 , val沒有被修改财饥。
如果從這個角度去看换吧,那么我們可以認(rèn)為python函數(shù)參數(shù)傳遞的是值,而不是引用嗎钥星?答:不可以沾瓦,看下面一個例子
main函數(shù)里面一個為空的list,在函數(shù)里面去修改它打颤,如果是值傳遞暴拄,那么調(diào)用函數(shù)之后,val應(yīng)該不變编饺。如果是引用傳遞乖篷,那么val被修改,里面有一個“hello”透且。去運(yùn)行程序撕蔼,輸出的結(jié)果是“hello”(val已經(jīng)被修改)
so,問題就來了秽誊,為什么有的參數(shù)傳遞進(jìn)入鲸沮,就可以被修改,為什么有的參數(shù)傳遞進(jìn)入锅论,就不會被修改讼溺?
在《編寫高質(zhì)量的代碼——改成python程序的n個建議》書中,作者解釋了這個問題最易。
意思是怒坯,python的函數(shù)參數(shù)傳遞,既不是值傳遞藻懒,也不是引用傳遞剔猿。它的傳遞方式是”傳對象“。函數(shù)參數(shù)在傳遞的過程中嬉荆,將整個對象傳入归敬。
1.對可變對象的修改在函數(shù)外部以及內(nèi)部都可見;
2.對于不可變對象鄙早,由于不能真正的修改汪茧,往往是創(chuàng)建一個新的對象,然后通過賦值來實(shí)現(xiàn)蝶锋。陆爽,所以,外部是不可見的扳缕。
這也就解釋了慌闭,為什么如果val是一個數(shù)字的時候别威,函數(shù)內(nèi)部對val修改,外部并不可見驴剔。如果val是一個list省古,那么對val修改,那么外部是可見的丧失。
so豺妓,問題又來了,什么是可變對象布讹,什么是不可變對象琳拭?
python不可變對象:int,string,float,tuple
python可變對象:dict,list
當(dāng)修改一個不可變對象的時候:
有i和j倆個變量的值為77,通過打印77的ID和變量i描验,j在內(nèi)存中的id我們得知它們都是指向同一塊內(nèi)存白嘁。所以說i和j都是指向同一個對象的。然后我們修改j的值膘流,讓j的值+1.按道理j修改之后應(yīng)該i的值也發(fā)生改變的絮缅,因?yàn)樗鼈兌际侵赶虻耐粔K內(nèi)存,但結(jié)果是并沒有呼股。因?yàn)閕nt類型是不可變類型耕魄,所有其實(shí)是j復(fù)制了一份到新的內(nèi)存地址然后+1司草,然后j又指向了新的地址荣回。所以j的內(nèi)存id發(fā)生了變化。
so愧怜,小結(jié)一下缠局,修改一個不可變對象的時候奄抽,會創(chuàng)建一個新的對象,然后指過去甩鳄。所以,id會改變额划。
對于一個可變對象妙啃,傳遞參數(shù)的時候,對它的修改是不會改id的俊戳。
def test_function(val):
print (id(val))
val=300
print (id(val))
def test_function2(val):
val.append('hello')
print (id(val))
if __name__=="__main__":
"""
val=[]
print (id(val))
test_function2(val)
"""
val=3
print (id(val))
test_function(val)
小結(jié)一下:
- python函數(shù)傳遞參數(shù)傳遞的是對象
- 對象分為可變對象和不可變對象揖赴,如果修改一個不可變對象的時候,會創(chuàng)建一個新的對象抑胎。如果修改一個可變對象燥滑,那么就在原來的對象上的修改。
給一個例子阿逃,分析一下:(《編寫高質(zhì)量的代碼》書上面的例子)
下一個問題铭拧,對象的拷貝赃蛛,什么是深拷貝or淺拷貝?
前幾次寫程序幾個小bug都是由于對 對象的賦值搀菩,淺拷貝呕臂,深拷貝的概念沒有搞透徹。
-
賦值
創(chuàng)建一個叫will的list肪跋,然后讓will賦值給wilber歧蒋。然后,看一下這兩個id州既,是完全相同的谜洽。
然后,看一下list里面的元素的id是不是相同吴叶。
答案也是完全相同的阐虚。
我們試著修改一下list里面的東西:
will[0] = "Wilber"
will[2].append("CSS")
修改之后,發(fā)現(xiàn)will[0]的id改變了晤郑,但是will[2]的id沒有變敌呈。
因?yàn)閣ill[0]是一個不可變的對象,所以修改之后id就可變造寝。will[2]是一個可變對象磕洪,修改之后id不變。
#給個例子 可以跑一下 看看
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = will
print (id(will))
print (will)
print ([id(ele) for ele in will])
print (id(wilber))
print (wilber)
print ([id(ele) for ele in wilber])
print ("***update*****")
will[0] = "Wilber"
will[2].append("CSS")
print (id(will))
print (will)
print ([id(ele) for ele in will])
print (id(wilber))
print (wilber)
print ([id(ele) for ele in wilber])
結(jié)果:
- 淺拷貝
注意诫龙,切片也是淺拷貝析显。
import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
- 深拷貝
import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
參考:
- 《編寫高質(zhì)量的代碼》
- 可變對象and不可變對象 : http://www.reibang.com/p/c5582e23b26c
- 深拷貝and淺拷貝 :
http://www.cnblogs.com/wilber2013/p/4645353.html