8. 參數接收的是引數的「參照」

(1) 函式更改了引數內容

呼叫函式時,系統會將引數的「參照」傳給參數,而「參照」(reference)的內容是指向存在於記憶體中的物件。如果引數是一個list,參數會得到指向同一個list的參照;由於list是一種「可更改內容」(mutable)的物件,因此若在函式中更改list參數內容,作為引數的物件內容也會一起被更改,因為參數及引數其實都是指向同一個mutable物件。

以下是一個例子:

假設傳入一個成績的list,函式將同一個list內的成績轉成等級,再回傳該list的參照。

def rank(scores):
    '''將分數轉成等級分數並回傳'''
    for i in range(len(scores)):
        if scores[i] >= 90:
            scores[i] = 'A'
        elif scores[i] >= 75:
            scores[i] = 'B'
        elif scores[i] >= 60:
            scores[i] = 'C'
        else:
            scores[i] = 'D'
    
    return scores

呼叫函式:

sco = [90, 80, 60, 50, 100, 70]
print('原始分數:', sco)
print('等級分數:', rank(sco))
print('原始分數:', sco)

請注意第4行的輸出,它的內容被函式內的指令改變了。

執行結果:

原始分數: [90, 80, 60, 50, 100, 70] 等級分數: ['A', 'B', 'C', 'D', 'A', 'C'] 原始分數: ['A', 'B', 'C', 'D', 'A', 'C']

說明:

(2) 讓參數使用引數的「複本」

如果不希望函式改變了原本引數內容,可以在函式中先產生一個「複本」,這樣就有兩個不同的物件,原引數的內容不會被更改。

以下是一個例子:

假設傳入一個成績的list,函式先產生list的複本,然後在複本中將成績轉成等級,再回傳複本的參照。

def rank(scores):
    '''回傳等級分數'''
    scores2 = scores.copy() #產生一個複本
    
    for i in range(len(scores2)):
        if scores2[i] >= 90:
            scores2[i] = 'A'
        elif scores2[i] >= 75:
            scores2[i] = 'B'
        elif scores2[i] >= 60:
            scores2[i] = 'C'
        else:
            scores2[i] = 'D'
    
    return scores2

請注意,第3行的寫法是「淺拷貝」(shallow copy),表示如果list之內還有list,那麼內層的list仍是共有,並不會產生內層list的複本。

呼叫函式:

sco = [90, 80, 60, 50, 100, 70]
print('原始分數:', sco)
print('等級分數:', rank(sco))
print('原始分數:', sco)

執行結果:

原始分數: [90, 80, 60, 50, 100, 70] 等級分數: ['A', 'B', 'C', 'D', 'A', 'C'] 原始分數: [90, 80, 60, 50, 100, 70]

說明:

問題:

dict(字典)也是一種mutable物件,請寫一個函式將字典中的value由小寫改成大寫,確認在不產生複本的情況下,原引數內容也會被更改。

測試主程式:

fruits = {'蘋果':'apple', '香蕉':'banana', '橘子':'orange'}
print('原始字典:', fruits)
print('大寫字典:', toUpper(fruits))
print('原始字典:', fruits)

測試結果範例:

原始字典: {'蘋果': 'apple', '香蕉': 'banana', '橘子': 'orange'} 大寫字典: {'蘋果': 'APPLE', '香蕉': 'BANANA', '橘子': 'ORANGE'} 原始字典: {'蘋果': 'APPLE', '香蕉': 'BANANA', '橘子': 'ORANGE'}