python調(diào)用的函數(shù),傳遞參數(shù)的時候,是傳值還是傳遞引用?

對于一段這樣的代碼,在main函數(shù)里面創(chuàng)建一個對象val=3,然后在test_function 里面把它修改成300,然后在main函數(shù)里面輸出,它的值應該是3還是300?
答案:3 , val沒有被修改。
如果從這個角度去看,那么我們可以認為python函數(shù)參數(shù)傳遞的是值,而不是引用嗎?答:不可以,看下面一個例子

main函數(shù)里面一個為空的list,在函數(shù)里面去修改它,如果是值傳遞,那么調(diào)用函數(shù)之后,val應該不變。如果是引用傳遞,那么val被修改,里面有一個“hello”。去運行程序,輸出的結(jié)果是“hello”(val已經(jīng)被修改)
so,問題就來了,為什么有的參數(shù)傳遞進入,就可以被修改,為什么有的參數(shù)傳遞進入,就不會被修改?
在《編寫高質(zhì)量的代碼——改成python程序的n個建議》書中,作者解釋了這個問題。

意思是,python的函數(shù)參數(shù)傳遞,既不是值傳遞,也不是引用傳遞。它的傳遞方式是”傳對象“。函數(shù)參數(shù)在傳遞的過程中,將整個對象傳入。
1.對可變對象的修改在函數(shù)外部以及內(nèi)部都可見;
2.對于不可變對象,由于不能真正的修改,往往是創(chuàng)建一個新的對象,然后通過賦值來實現(xiàn)。,所以,外部是不可見的。
這也就解釋了,為什么如果val是一個數(shù)字的時候,函數(shù)內(nèi)部對val修改,外部并不可見。如果val是一個list,那么對val修改,那么外部是可見的。
so,問題又來了,什么是可變對象,什么是不可變對象?
python不可變對象:int,string,float,tuple
python可變對象:dict,list
當修改一個不可變對象的時候:

有i和j倆個變量的值為77,通過打印77的ID和變量i,j在內(nèi)存中的id我們得知它們都是指向同一塊內(nèi)存。所以說i和j都是指向同一個對象的。然后我們修改j的值,讓j的值+1.按道理j修改之后應該i的值也發(fā)生改變的,因為它們都是指向的同一塊內(nèi)存,但結(jié)果是并沒有。因為int類型是不可變類型,所有其實是j復制了一份到新的內(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都是由于對 對象的賦值,淺拷貝,深拷貝的概念沒有搞透徹。
-
賦值
image.png
創(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沒有變。
因為will[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.itdecent.cn/p/c5582e23b26c
- 深拷貝and淺拷貝 :
http://www.cnblogs.com/wilber2013/p/4645353.html
