說明:本文主要以理解 Python 中的賦值、參數傳遞運行機製為主,可能其中的觀點不一定嚴謹,如果有不對的地方,還望指出,先謝過啦
在學習編程的過程我們都會遇到很多定義,之前在遇到這些定義的時候,我有一種強迫症。就是不搞清楚每一個字的含義,不善罷甘休。但是每次都會盡興而來,失望而歸。多次之後我學乖了,就是不糾結實際每一個字的含義,用自己能懂的方式理解他們,比如今天要說的引用傳遞和值傳遞
官方的定義是這樣的
值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數 —— via:百度百科
引用傳遞: 引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數—— via:百度百科
引用傳遞:
引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數—— via:百度百科
這種每一個字我都認識,但連起來我就不知道啥意思的感覺,已經伴隨了我這個9年+4年教育生涯,至此對它深惡痛絕。
回到正題,我們暫時拋開兩個概念,我們先來說下 Python 中的賦值,以我的理解,其實就是下定義的步驟,如果大家看過《武林外傳》中第二十九集《呂聖人智斗姬無命 佟掌柜火拚展紅綾》,就更容易理解以下的概念了
賦值這個操作,其實可以理解成給物體貼標籤,或者可以理解為給物體命名,既然是名稱,就像《武林外傳》中的一樣,你可以叫「姬無命」,我也可以叫「姬無命」。重要的是這個物體,而不是標籤。其中,這個標籤(或者名稱),我們在計算機中把它叫做變數,物體就是實際的值
我們知道,在計算機中,值會佔用一定的空間去存儲,而計算機為了方便找到它,則會給它一個地址,方便我們找到它
我們寫段代碼理解下
>>> zxj = "張小雞" >>> jwm = zxj >>> print(id(zxj)) 4334207504 >>> print(id(jwm)) 4334207504
看圖理解很簡單,這裡的物體是字元「張小雞」,我們把它貼上標籤「zxj」,第二個地方賦值就相當於再貼一個標籤「jwm」。就是上面說的,你可以叫「張小雞」,我也可以叫「張小雞」,看後面,他們實際上的內存地址都是一樣的
在理解了上面的過程,我們再來看看 Python 中調用函數傳遞參數的過程。先說結論,Python 中參數的傳遞就是賦值的過程。我們來看下這段代碼
a = "張小雞" ? def foo(b): print ">>> before id(b):", id(b) b = "姬無命" print ">>> after id(b):", id(b) ? print ">>> before id(a):", id(a) foo(a) print ">>> after id(a):", id(a)
這一段的輸出如下
>>> before id(a): 4301385520 >>> before id(b): 4301385520 >>> after id(b): 4301385040 >>> after id(a): 4301385520
我們在賦值時,其實就相當於把函數的形參b這個標籤又貼在了「張小雞」物體上。後面我們再執行b=「姬無命」時,就相當於把b這個標籤從「張小雞」這個物體上撕下來,放到「姬無命」這個物體上
Python 內部對對象進行了區分,即為可變對象和不可變對象,類型如下
int、str、float、tuple等為不可變對象
list、dict、set等為可變對象
不可變對象我們上面已經說過他的賦值的特點,我們這裡主要看可變對象。對於可變對象,我們可以簡單的理解為做了個包裝盒。我們在賦值的時候,這個標籤是貼在了這個包裝盒子上。計算機會記錄這個盒子的地址,裡面每一個物體的地址,計算機也仍然會記錄
a = ["張小雞"] ? def foo(b): print(">>> before id(b):", id(b)) b[0] = "姬無命" b.append("Tom") print(">>> after id(b):", id(b)) ? print(">>> before value(a):", a) print(">>> before id(a):", id(a)) foo(a) print(">>> after value(a):", a) print(">>> after id(a):", id(a))
輸出如下
>>> before value(a): [張小雞] >>> before id(a): 4518129480 >>> before id(b): 4518129480 >>> after id(b): 4518129480 >>> after value(a): [姬無命, Tom] >>> after id(a): 4518129480
我們將盒子裡面的「張小雞」替換為「姬無命」,又再盒子裡面添加了「Tom」,自始至終,因為我們沒有動過盒子本身,所以他的地址不會發生變化
結合上圖來看下,我們修改一下代碼,再深入看下盒子和盒子裡面物體的地址的變化
a = ["張小雞"] ? def foo(b): b[0] = "姬無命" b.append("Tom") ? print(">>> before id(a): ", id(a)) print(">>> before id(a[:]): ", [id(_) for _ in a]) foo(a) print(">>> after id(a): ", id(a)) print(">>> after id(a[:]): ", [id(_) for _ in a])
>>> before id(a): 4471127880 >>> before id(a[:]): [4472230896] >>> after id(a): 4471127880 >>> after id(a[:]): [4472230992, 4472127648]
看,我們的盒子(即id(a))自始至終都沒有變化,而內部因為更換過物體,所以裡面的地址都不一樣了
def foo(a, b=[]): b.append(a) return b ? print(foo(1)) print(foo(1)) print(foo(1))
上面這段代碼的輸出結果是
>>> [1] >>> [1, 1] >>> [1, 1, 1]
能說說為什麼會發生這樣的怪現象嗎?並想一想這個特性的應用場景有哪些?歡迎評論留言給我
Python參數傳遞,既不是傳值也不是傳引用
python函數傳參是傳值還是傳引用?
python可變和不可變對象
微信公眾號:雞仔說(ID: jizaishuo)
推薦閱讀:
TAG:Python入門 | Python | Python開發 |