【正文部分】
值傳遞(passl-by-value)過程中,被調函數的形式參數作為被調函數的局部變量處理,即在堆棧中開辟了內存空間來存放由主調函數放進來的實參的值,從而想成為了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作為局部變量進行,不會影響主調函數的實參變量的值。【就給你一個值,你自己做個副本就好】
引用傳遞(pass-by-reference)過程中,被調函數的形式參數雖然也作為局部變量在堆棧中開辟了內存空間,但這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接尋址,即通過堆棧中存放的地址訪問主調函數中的實參變量。正因為如此,被調函數對形參做任何的操作都影響了主調函數中的實參變量。【給你一份我的地址,可以根據這個隨時找我,你還能在我家吃個飯啥的】
名詞解釋:
1、變量與對象
Python 中一切皆為對象,數字是對象,列表是對象,函數也是對象,任何東西都是對象。而變量只是對象的一個引用,對象的操作都是通過引用來完成的。而數據類型是針對【對象】而言的,不是針對【變量】。
2、引用:
可以理解為對對象的內存地址指向。
3、賦值
賦值操作的本質就是綁定一個新的對象。
對於不可變數據類型,相當於綁定了一個新的對象,所以進行到是值傳遞的形式,賦值操作完成之后變量引用的對象發生了變化。args=2執行之前,arg拿到了實參變量a的值,成為一個副本對象,對arg進行賦值操作的時候,都是只作為局部變量進行,不會對主函數的實參變量a造成影響。
def foo(arg): arg = 2 print(arg) a = 1 foo(a) # 輸出:2 print(a) # 輸出:1
而對於可變數據類型,具有增刪改查的特性,進行增刪改查的時候並沒有賦值操作,所以沒有綁定新的對象,對同一個引用或稱為同一個對象進行增刪改查之后,變量指向的還是該對象。所以進行的是引用傳遞的形式。arg在執行append之前,實參變量b把自己引用對象的地址也給了arg,所以對arg進行操作的時候對引用對象b也進行了修改。
def bar(args): args.append(1) b = [] print(b)# 輸出:[] print(id(b)) # 輸出:4324106952 bar(b) print(b) # 輸出:[1] print(id(b)) # 輸出:4324106952
結論:
python不允許程序員選擇采用傳值還是傳引用。python參數傳遞采用的肯定是“傳對象引用”的方式。這種方式相當於傳值和傳引用的一種綜合。
如果函數收到的是一個不可變對象(數字、字符或元組)的引用,就不能直接修改原始對象--相當於通過‘值傳遞’來傳遞對象。
如果函數收到的是一個可變對象(字典、列表)的引用,就能修改對象的原始值--相當於‘傳引用’來傳遞對象。