通過學習《Python函數值傳遞和引用傳遞》一節我們知道,根據實際參數的類型不同,函數參數的傳遞方式分為值傳遞和引用傳遞(又稱為地址傳遞),Python 底層是如何實現它們的呢?Python 中函數參數由實參傳遞給形參的過程,是由參數傳遞機制來控制的。
本節將圍繞值傳遞和引用傳遞,深度剖析它們的底層實現。
## Python函數參數的值傳遞機制
Python 函數參數的值傳遞,其本質就是將實際參數值復制一份,將其副本傳給形參。這意味著,采用值傳遞方式的函數中,無論其內部對參數值進行如何修改,都不會影響函數外部的實參。
> 值傳遞的方式,類似于《西游記》里的孫悟空,它復制一個假孫悟空,假孫悟空具有的能力和真孫悟空相同,可除妖或被砍頭。但不管這個假孫悟空遇到什么事,真孫悟空都不會受到任何影響。與此類似,傳入函數的是實際參數值的復制品,不管在函數中對這個復制品如何操作,實際參數值本身不會受到任何影響。
下面程序演示了函數參數進行值傳遞的效果:
```
def swap(a , b) :
'''下面代碼實現a、b變量的值交換'''
a, b = b, a
print("swap函數里,a =", a, " b =", b)
a = 6
b = 9
swap(a , b)
print("函數外部 a =", a ," b =", b)
```
運行上面程序,將看到如下運行結果:
```
swap函數里,a = 9? b = 6
函數外部 a = 6? b = 9
```
從上面的運行結果來看,在 swap() 函數里,經過交換形參 a 和? b 的值,它們的值分別變成了 9 和 6,但函數外部變量 a 和 b 的值依然是 6 和 9。這也證實了,swap() 函數的參數傳遞機制,采用的是值傳遞,函數內部使用的形參 a 和 b,和實參 a、b 沒有任何關系。
```
swap() 函數中形參 a 和 b,各自分別是實參 a、b 的復制品。
```
如果讀者依舊不是很理解,下面通過示意圖來說明上面程序的執行過程。
上面程序開始定義了 a、b 兩個局部變量,這兩個變量在內存中的存儲示意圖如圖 1 所示。

圖 1 主棧區中 a、b 變量存儲示意圖
當程序執行 swap() 函數時,系統進入 swap() 函數,并將主程序中的 a、b 變量作為參數值傳入 swap() 函數,但傳入 swap() 函數的只是 a、b 的副本,而不是 a、b 本身。進入 swap() 函數后,系統中產生了 4 個變量,這 4 個變量在內存中的存儲示意圖如圖 2 所示。

圖 2 主棧區的變量作為參數值傳入 swap() 函數后存儲示意圖
當在主程序中調用 swap() 函數時,系統分別為主程序和 swap() 函數分配兩塊棧區,用于保存它們的局部變量。將主程序中的 a、b 變量作為參數值傳入 swap() 函數,實際上是在 swap() 函數棧區中重新產生了兩個變量 a、b,并將主程序棧區中 a、b 變量的值分別賦值給 swap() 函數棧區中的 a、b 參數(就是對 swap() 函數的 a、b 兩個變量進行初始化)。此時,系統存在兩個 a 變量、兩個 b 變量,只是存在于不同的棧區中而己。
程序在 swap() 函數中交換 a、b 兩個變量的值,實際上是對圖 2 中灰色區域的 a、b 變量進行交換。交換結束后,輸出 swap() 函數中 a、b 變量的值,可以看到 a 的值為 9,b 的值為 6,此時在內存中的存儲示意圖如圖 3 所示。

圖 3 swap() 函數中 a、b 交換之后的存儲示意圖
對比圖 3 與圖 1,可以看到兩個示意圖中主程序棧區中 a、b 的值并未有任何改變,程序改變的只是 swap() 函數棧區中 a、b 的值。這就是值傳遞的實質:當系統開始執行函數時,系統對形參執行初始化,就是把實參變量的值賦給函數的形參變量,在函數中操作的并不是實際的實參變量。
## Python函數參數的引用傳遞
如果實際參數的數據類型是可變對象(列表、字典),則函數參數的傳遞方式將采用引用傳遞方式。
下面程序示范了引用傳遞參數的效果:
```
def swap(dw):
# 下面代碼實現dw的a、b兩個元素的值交換
dw['a'], dw['b'] = dw['b'], dw['a']
print("swap函數里,a =", dw['a'], " b =", dw['b'])
dw = {'a': 6, 'b': 9}
swap(dw)
print("外部 dw 字典中,a =", dw['a']," b =",dw['b'])
```
運行上面程序,將看到如下運行結果:
```
swap 函數里,a = 9? b = 6
外部 dw 字典中,a = 9? b = 6
```
從上面的運行結果來看,在 swap() 函數里,dw 字典的 a、b 兩個元素的值被交換成功。不僅如此,當 swap() 函數執行結束后,主程序中 dw 字典的 a、b 兩個元素的值也被交換了。
注意,這里這很容易造成一種錯覺,讀者可能認為,在此 swap() 函數中,使用 dw 字典,就是外界的 dw 字典本身,而不是他的復制品。這只是一種錯覺,實際上,引用傳遞的底層實現,依舊使用的是值傳遞的方式。下面還是結合示意圖來說明程序的執行過程。
程序開始創建了一個字典對象,并定義了一個 dw 引用變量(其實就是一個指針)指向字典對象,這意味著此時內存中有兩個東西:對象本身和指向該對象的引用變量。此時在系統內存中的存儲示意圖如圖 4 所示:

圖 4 主程序創建了字典對象后存儲示意圖
接下來主程序開始調用 swap() 函數,在調用 swap() 函數時,dw 變量作為參數傳入 swap() 函數,這里依然采用值傳遞方式:把主程序中 dw 變量的值賦給 swap() 函數的 dw 形參,從而完成 swap() 函數的 dw 參數的初始化。值得指出的是,主程序中的 dw 是一個引用變量(也就是一個指針),它保存了字典對象的地址值,當把 dw 的值賦給 swap() 函數的 dw 參數后,就是讓 swap() 函數的 dw 參數也保存這個地址值,即也會引用到同一個字典對象。圖 5 顯示了 dw 字典傳入 swap() 函數后的存儲示意圖。

圖 5 dw 字典傳入 swap() 函數后存儲示意圖
從圖 5 來看,這種參數傳遞方式是不折不扣的值傳遞方式,系統一樣復制了dw 的副本傳入 swap() 函數。但由于 dw 只是一個引用變量,因此系統復制的是 dw 變量,并未復制字典本身。
當程序在 swap() 函數中操作 dw 參數時,由于 dw 只是一個引用變量,故實際操作的還是字典對象。此時,不管是操作主程序中的 dw 變量,還是操作 swap() 函數里的 dw 參數,其實操作的都是它們共同引用的字典對象,它們引用的是同一個字典對象。因此,當在 swap() 函數中交換 dw 參數所引用字典對象的 a、b 兩個元素的值后,可以看到在主程序中 dw 變量所引用字典對象的 a、b 兩個元素的值也被交換了。
為了更好地證明主程序中的 dw 和 swap() 函數中的 dw 是兩個變量,在 swap() 函數的最后一行增加如下代碼:
```
#把dw 直接賦值為None,讓它不再指向任何對象
dw = None
```
運行上面代碼,結果是 swap() 函數中的 dw 變量不再指向任何對象,程序其他地方沒有任何改變。主程序調用 swap() 函數后,再次訪問 dw 變量的 a、b 兩個元素,依然可以輸出 9、6。可見,主程序中的 dw 變量沒有受到任何影響。實際上,當在 swap() 函數中增加“dw =None”代碼后,在內存中的存儲示意圖如圖 6 所示。

圖 6 將 swap() 函數中的 dw 賦值為 None 后存儲示意圖
從圖 6 來看,把 swap() 函數中的 dw 賦值為 None 后,在 swap() 函數中失去了對字典對象的引用,不可再訪問該字典對象。但主程序中的 dw 變量不受任何影響,依然可以引用該字典對象,所以依然可以輸出字典對象的 a、b 元素的值。
通過上面介紹可以得出如下兩個結論:
1. 不管什么類型的參數,在 Python 函數中對參數直接使用“=”符號賦值是沒用的,直接使用“=”符號賦值并不能改變參數。
2. 如果需要讓函數修改某些數據,則可以通過把這些數據包裝成列表、字典等可變對象,然后把列表、字典等可變對象作為參數傳入函數,在函數中通過列表、字典的方法修改它們,這樣才能改變這些數據。
- 一、Python新手篇
- 第1章 環境配置
- 1.1 順序結構流程圖
- 1.2 分支結構流程圖
- 1.3循環結構流程圖
- 第2章-小海龜畫圖
- 2.1 認識會作圖的小海龜
- 2.2小海龜的作圖絕學
- 2.2.1小海龜畫線段
- 2.2.2小海龜畫正方形
- 2.2.3小海龜畫長方形
- 2.2.4小海龜畫小星星
- 2.2.5添加背景色
- 2.2.6給小星星涂色
- 2.3小海龜小結
- 2.4小海龜作圖實踐
- 2.5繪圖練習
- 2.5.1繪制三角形
- 2.5.2繪制倒三角+正三角
- 2.5.3繪制正方形
- 2.5.4繪制四條線
- 2.5.5 畫五角星
- 2.5.6 畫五個同心圓
- 2.5.7 畫一個回型
- 2.5.8 繪制復雜圖形
- 2.5.9 繪制太陽花
- 2.5.10 繪制4個不同半徑的同切圓
- 2.5.11 六角形的繪制
- 2.5.12 繪制一個風輪
- 2.5.13繪制文本
- 2.5.14 繪制菱形
- 2.5.15 繪制正五邊形
- 2.5.16 繪制一個四瓣花圖形
- 2.5.17 繪制一個四葉草
- 2.5.18 繪制一個星星
- 2.5.19 繪制一條綠色蟒蛇
- 2.5.20 繪制一朵小紅花
- 2.5.21 文字順時針呈圓形排列
- 2.5.22 多變螺旋線
- 2.5.23 順序結構繪制圖形
- 2.5.24橫切開的橙子
- 2.5.25繪制扇子
- 2.5.26繪制棒棒糖
- 2.5.27繪制螺旋彩色文字
- 2.5.28寫春聯
- 2.5.29繪制奧運五環
- 2.5.30紅燈籠
- 2.5.31寫古詩
- 2.5.32寫福字
- 2.5.33冰墩墩
- 2.5.34玫瑰花
- 2.5.35丘比特愛心之箭
- 2.5.36櫻花樹
- 2.5.37繪制旋轉風車
- 第3章這是變量
- 3.1神奇的變量
- 3.2數字的奧秘
- 3.2.1數字運算符
- 3.2.2.運算的順序
- 3.2.3給數字取個洋氣的英文名
- 3.3字符串是什么東西
- 3.4變量的可變性
- 3.5變量取名字很講究
- 3.6變量學習小結
- 3.7趣味小挑戰
- 第4章 是或不是的前因后果
- 4.1 什么叫條件判斷
- 4.1.1猜數字
- 4.1.2坐火車
- 4.2 看if來斷案
- 4.3 真假總該做點事
- 4.4 if不做的,else來做
- 4.5 它們還有一個兄弟elif
- 4.6 滿足兩個條件找and
- 4.7 滿足一個條件用or
- 4.8 邏輯運算符not
- 4.9 小結條件邏輯
- 4.10 條件邏輯大考驗
- 第5章 循環是一種神奇的力量
- 5.1 循環內功修煉,掌握for循環
- 5.2 循環招式升級while
- 5.3 可怕的無限循環
- 5.4 break和continue
- 5.5 溫故而知新
- 5.6 循環大測試
- 5.7 循環
- 5.7.1導學
- 5.7.2教學設計
- 5.7.3 課件
- 第6章 3兄弟:“列表” “元組” “字典”
- 6.1 重新認識列表
- 6.2 往列表里添加新元素
- 6.3 確定列表中元素的位置
- 6.4 獲取列表中連續的元素
- 6.5 換掉列表中的元素
- 6.6 查詢列表中是否存在該元素
- 6.7 找到列表元素的索引
- 6.8 遍歷列表中的所有元素
- 6.9 給列表元素排序
- 6.10 元組是只讀的
- 6.11 字典講究對應
- 6.11.1 往字典中添加新元素
- 6.11.2 從字典中獲取元素
- 6.11.3 修改字典中元素的值
- 6.11.4 刪除字典中的元素
- 6.11.5 遍歷字典中的元素
- 6.12 課后小結
- 6.13 迎接小挑戰
- 第7章 擁有強大能量的函數
- 7.1 創造自己的函數
- 7.2 讓函數動起來
- 7.3 有參數的函數
- 7.4 有多個參數的函數
- 7.5 參數數量不確定
- 7.6 有返回值的函數
- 7.7 變量的作用域
- 7.7.1 局部變量
- 7.7.2 全局變量
- 7.7.3 強制為全局變量
- 7.8 函數能量回收
- 7.9 函數能量小挑戰
- 第8章 深奧的類與對象
- 8.1 我們熟悉的類與對象
- 8.2 Python中的類和對象
- 8.3 創建實例對象
- 8.4 對象都有自己獨特的屬性
- 8.5 對象還可以有自己的動作
- 8.6 類的三大特性
- 8.7 類與對象總結
- 8.8 類與對象小挑戰
- 第9章 注釋幫助我們理解
- 9.1 如何創建注釋
- 9.1.1 單行注釋
- 9.1.2 多行注釋
- 9.2 添加注釋的"要”與“不要”
- 9.3 注釋回顧
- 9.4 添加注釋
- 第10章 警報,警報,發現異常
- 10.1 Python的守衛者
- 10.2 調試
- 10.3 異常與調試小結
- 10.4 異常與調試挑戰
- 第11章 強大的模塊功能庫
- 11.1 什么是模塊
- 11.2 創建屬于自己的模塊
- 11.3 使用模塊帶來的便利
- 11.4 命名空間
- 11.5 局部命名空間
- 11.6 全局命名空間
- 11.7 內置命名空間
- 11.8 Python內置標準模塊
- 11.9 模塊學習總結
- 11.10 模塊學習大挑戰
- 第12章 玩轉圖形界面編程
- 12.1 什么是GUI編程
- 12.2 Python中的GUI編程
- 12.3 第一個GUI程序----根窗口
- 12.4 Label組件
- 12.5 Button組件
- 12.6 Entry組件
- 12.7 Canvas組件
- 12.8 布局管理方式
- 12.9 tkinter小總結
- 12.10 tkinter小挑戰
- 第13章 操控文件的讀與寫
- 13.1 什么是文件
- 13.2 打開文件
- 13.3 寫文件
- 13.4 讀文件
- 13.5 游戲時間
- 13.6 文件小總結
- 13.7 文件小挑戰
- 第14章 網絡爬蟲不是小蟲子
- 14.1 網絡連接
- 14.2 了解網絡爬蟲
- 14.3 用Python發起網絡請求
- 14.4 HTML
- 14.4.1 HTML的常用標簽
- 14.4.2 標簽的樣式
- 14.5 解析網站內容
- 14.6 第一個爬蟲程序
- 14.7 爬蟲小總結
- 14.8 爬蟲小挑戰
- 第15章 攻克星球大戰
- 15.1 pygame的安裝
- 15.2 分析一下我們的飛機大戰
- 15.3 定義運行窗口
- 15.4 用鍵盤控制飛機移動
- 15.5 飛機發射子彈
- 15.6 敵機的創建
- 15.7 子彈擊中敵機----精靈的碰撞檢測
- 15.8 記錄得分
- 15.9 游戲結束
- 15.10 pygame小結
- 15.11 pygame課后小挑戰
- 三、Python基礎篇
- 4.列表、元組、字典和集合
- 4.1.什么是序列,Python序列詳解
- 4.2.Python列表(list)
- 4.3Python list列表添加元素
- 4.4Python list列表刪除元素
- 4.5Python list列表修改元素
- 4.6Python list列表查找元素
- 5
- 6
- 7.函數和lambda表達式
- 7.1Python函數
- 7.2Python函數值傳遞和引用傳遞(包括形式參數和實際參數的區別)
- 7.3Python函數參數傳遞機制(超級詳細)
- 7.4什么是位置參數,Python位置參數
- 7.5Python函數關鍵字參數及用法
- 7.6Python函數默認參數設置(超級詳細)
- 7.7Python函數可變參數(*args,**kwargs)詳解
- 7.8Python逆向參數收集詳解(進階必讀)
- 7.9Python None(空值)及用法
- 7.10Python return函數返回值詳解
- 7.11Python函數返回多個值的方法(入門必讀)
- 7.12Python partial偏函數及用法
- 7.13Python函數遞歸
- 7.14Python變量作用域(全局變量和局部變量)
- 8.Python類和對象
- 8.1什么是面向對象,Python面向對象(一切皆對象)
- 8.2Python class:定義類(入門必讀)
- Python考級
- Python一級
- Python一級202206
- Python一級202203
- Python一級202112
- Python一級202109
- Python一級202106
- Python一級202103
- Python一級202012
- Python一級202009
- Python一級202006
- Python二級
- Python二級202206
- Python二級202203
- Python二級202112
- Python二級202109
- Python三級
- 1.202109Python三級
- Python四級
- 1.202109Python四級
- Python練習題
- 參考
- 1.繪制三角形
- 2.繪制倒三角+正三角
- 3.繪制正方形
- 4.繪制四條線段
- 5.畫五角星
- 6.畫五個同心圓
- 7.畫一個回型
- 8.繪制如下圖形
- 9.繪制太陽花
- 10.繪制4個不同半徑的同切圓
- 11.六角形的繪制
- 12.繪制一個風輪
- 13 繪制文本
- 14 繪制菱形
- 15.繪制正五邊形
- 16 繪制一個四瓣花圖形
- 17 繪制一個四葉草
- 18 繪制一個星星
- 19 繪制一條綠色蟒蛇
- 20 繪制一朵小紅花
- 21 文字順時針呈圓形排列
- 22 多變螺旋線
- 23 順序結構繪制圖形
- 24橫切開的橙子
- 25繪制扇子
- 26繪制棒棒糖
- 27.彩色螺旋文字
- 28寫春聯
- 29繪制奧運五環
- 30紅燈籠
- 31寫古詩
- 32寫福字
- 33冰墩墩
- 34玫瑰花
- 35丘比特愛心之箭
- 36隨機櫻花樹
- 37旋轉風車
- 分數等級
- 自由落體運動
- 根據年月日計算天數
- 1.常見數學問題
- Python庫學習
- 1.turtle庫
- 2.sprites庫
- 參考資料
- Python編程入門與算法進階
- Python編程一級
- 第1課編程環境
- 第2課編程基礎
- 第3課運算符
- 第4課Turtle庫
- Python編程二級
- 第5課基本數據類型
- 第6課可變序列-列表
- 第7課不可變序列-元祖
- 第8課字符串
- 第9課字典
- 第10課流程控制
- 第11課計算思維
- Python編程三級
- 第12課編碼與數制
- 第13課數據處理
- 第14課異常處理
- 第15課算法
- 第16課核心函數
- Python編程四級
- 第17課函數的相關概念
- 第18課自定義函數的創建與調用
- 第19課遞歸與遞推
- 第20課分治算法
- 第21課算法優化
- 第22課第三方庫(模塊)的獲取、安裝與調用
- Python編程五級
- Python編程六級
- 常用案例
- 高一信息技術試講
- 2.3教案
- 教案