2017-08-02 23 views
0

我本來期望下面的代碼:爲什麼更改對象類型會影響Python在傳遞引用時的行爲?

def a(l): 
    l.append(3) 

def b(l): 
    l = 5 

def c(l): 
    l = (2,-2) 

numbers = [1,2,3,4,5] 
a(numbers) 
print(numbers) 
b(numbers) 
print(numbers) 
c(numbers) 
print(numbers) 

打印:

[1, 2, 3, 4, 5, 3] 
5 
(2,-2) 

但它打印:

[1, 2, 3, 4, 5, 3] 
[1, 2, 3, 4, 5, 3] 
[1, 2, 3, 4, 5, 3] 

爲什麼?

編輯:

這是因爲通之間的差異參考呼籲通過分享

+1

也許你很迷惑[*通過引用*](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference)與作爲**引用傳遞的對象**(即在Python中,對象是通過*作爲參考)? –

+0

在第一種情況下,您傳入可變對象並對其進行變異。其他情況下,您將本地名稱更改爲指向新值,並且這不會更改調用範圍,您需要'return l'將該值返回到調用範圍,例如, 'numbers = b(numbers)' – AChampion

+0

@AChampion它將不會更新原來的'l'變量。這需要將返回值分配給(全局)變量。 – JohanL

回答

2

第一種方法(a)實際上修改經由l.append(...)

無論是第二和第三種方法(bc)傳遞的列表項重新分配參數l到一個新的值。這個重新分配是函數的局部,不會返回,因此會丟失。在執行b(numbers)c(numbers)後打印numbers時,將打印由方法a修改的值。

0

Python中的大多數值都是不可變的,這意味着它們無法更新。這意味着變量的值不能改變。但是,可以創建一個新的(不可變的)值的參考點。因此,寫

i = 4 
i = 5 

當你實際上首先製造i點的(不可變的值)4,然後指着i的(同樣不可變的)價值5

列表,但是,是可變的。這意味着我們可以做

L = [3,4] 
L.append(5) 

,並有一個新的值[3, 4, 5]更新的同一個引用的對象。

當調用一個函數時, iL不直接傳遞給函數。而是傳遞值的引用。原始引用也保存在調用環境中,以在被調用函數返回時恢復。在你的函數

尋找這意味着:

def a(l): 
    l.append(3) 

這裏,參考l改變/更新,因爲追加將改變的是什麼參考價值。因此,從函數l返回時,將被「恢復」爲與函數中使用的值相同的值,並且列表被更新。

def b(l): 
    l = 5 

def c(l): 
    l = (2,-2) 

然而,在這兩個功能,參考l由指向新的東西,由於分配。因此我們修改了參考本身。然後,返回時,參考被恢復爲指向調用範圍中使用的值。因此,l將再次成爲原始列表,並且該函數中使用的參考值將超出範圍,並且該參考指向的任何值都將丟失/刪除。

因此,這不僅僅是比參照稱爲參考稱爲語義差別通過共享其具有的類型,以及如何引用在Python的工作不變性做稱爲多。

+0

很好的解釋。但是,請注意,如果在第一個函數「a」中重新分配了「l」,則無關緊要。使用這個修改過的函數''numbers'將會是相同的: '''def a(l): l.append(3); l = 0''' – Alexander

+0

@亞歷山大(和其他人)是的,你是對的。它與引用是相同的,因爲我們首先分配'l'指向一個新變量,def a(l):l = [] l.append(3)'不會更新原來的'l'。 – JohanL

+0

「調用函數時,參數(例如i和L)不作爲值傳遞給函數,而是傳遞參數的引用,但是原始引用保存在調用環境中,在調用時被恢復函數返回「。不可以。參數*被傳遞。參數*是*引用。 Python中的每個值(每個變量的值和每個表達式的結果)都是一個參考。沒有「拯救」或「恢復」任何東西。 – newacct

3

我認爲這是Python的,像C和Java,有效地傳遞值。在您的a()函數的情況下,您是傳遞值爲的引用。那裏沒有通過引用

Perl中,在另一方面,不通,通過該通過引用有一些類似的行爲,以通別名

我見過一位計算機科學教授錯誤地聲稱Objective-C通過引用傳遞,因爲教科書是這麼說的,沒有經過考慮。但它仍然只是傳遞價值。

相關問題