今天看到這樣一個問題: Python 的函數(shù)是怎么傳遞參數(shù)的掰伸,有了一些興趣,因為以前都是直接信的一個流傳度較廣的說法
對于不可變對象作為函數(shù)參數(shù)废士,相當于C系語言的值傳遞;
對于可變對象作為函數(shù)參數(shù)五续,相當于C系語言的引用傳遞。
那么事實上真是如此嗎龄恋?我們來看幾個例子疙驾。
首先我們來看看第一個說法,這里我實驗環(huán)境是Python3.6郭毕。
對于不可變對象作為函數(shù)參數(shù)進行傳遞战转,這里我分別以int
和tuple
進行測試
def test_immutable_for_simpledata(args):
print('============')
print(2, id(args))
args = args + 10
print(3, args)
print(4, id(args))
print('============')
if __name__ == '__main__':
cur = 5000
print(1, id(cur))
test_immutable_for_simpledata(cur)
print(5, cur)
print(6, id(cur))
結果如下
1 42301856
============
2 42301856
3 5010
4 42301616
============
5 5000
6 42301856
我們看第一步和第二步漏峰,兩者打印出來的地址是一樣的桶蝎,這可以證明Python對不可變類型傳參是使用的引用傳參痒钝,但是為什么函數(shù)中用了args+10
隅忿,函數(shù)外面的args
值還是沒變呢庭惜?我們可以看到第四步的args
的地址其實已經變了膳灶。即說明加10和未加10兩個時間點是兩個不同的對象攻柠。為什么是這樣呢踊谋?這就需要理解什么是不可變對象了蝉仇。在我理解來,不可變對象就是它的成員元素不變,比如對于簡單對象(如int
,float
)來說轿衔,就是它的值不變沉迹,對于復合類型(如tuple
)來說,就是它的成員元素(即地址)不變害驹”夼唬回到代碼中,這里是基本類型的不可變對象宛官,對它進行運算操作其實就是創(chuàng)建新的對象葫松,然后將原先的變量名綁定到新的對象上。讀者前一句話沒讀懂的話底洗,可以去了解一下Python的名字空間腋么。
為了加深讀者的理解,我們再以tuple
為例進行測試亥揖。代碼如下
def test_immutable_for_tuple(args):
print('============')
print(2, id(args))
args[0].append(5)
print(3, args)
print(4, id(args))
print('============')
if __name__ == '__main__':
cur = ([0], 1)
print(1, id(cur))
test_immutable_for_tuple(cur)
print(5, cur)
print(6, id(cur))
結果如下
1 33951020
============
2 33951020
3 ([0, 5], 1)
4 33951020
============
5 ([0, 5], 1)
6 33951020
這次的結果和上次明顯不同了珊擂。雖然tuple
是不可變對象,我們使用tuple
為參數(shù)傳入函數(shù)中费变,然后對tuple中的列表元素進行了操作摧扇,結果函數(shù)外部的變量也發(fā)生了變化。我們也可以看到參數(shù)的地址在函數(shù)內外其實都一樣挚歧。
根據(jù)上述測試扛稽,我們可以得出一個結論,對于不可變對象的函數(shù)傳參昼激,依然是傳的引用(地址)庇绽。對于簡單類型,在函數(shù)內對其操作之所以不會影響函數(shù)范圍外的值橙困,是因為運算中的賦值操作產生了新的對象瞧掺,而不是對原有對象的改變。而對于一些復雜類型凡傅,就可以比較清晰的看出辟狈,函數(shù)內參數(shù)的改變同樣會影響函數(shù)外的變量。
對于可變對象夏跷,我們同樣可以使用上述邏輯去驗證哼转,由于篇幅,我就不啰嗦了槽华。
最終可以得到的結論是壹蔓,Python中的函數(shù)通過引用傳參。