你真的理解Python中的賦值、傳參嗎?

說明:本文主要以理解 Python 中的賦值、參數傳遞運行機制爲主,可能其中的觀點不必定嚴謹,若是有不對的地方,還望指出,先謝過啦html

在學習編程的過程咱們都會遇到不少定義,以前在遇到這些定義的時候,我有一種強迫症。就是不搞清楚每個字的含義,不善罷甘休。可是每次都會盡興而來,失望而歸。屢次以後我學乖了,就是不糾結實際每個字的含義,用本身能懂的方式理解他們,好比今天要說的引用傳遞和值傳遞python

官方的定義是這樣的git

值傳遞:github

值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中若是對參數進行修改,將不會影響到實際參數 —— via:百度百科編程

引用傳遞:bash

引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數—— via:百度百科app

這種每個字我都認識,但連起來我就不知道啥意思的感受,已經伴隨了我這個9年+4年教育生涯,至此對它深惡痛絕。函數

賦值

回到正題,咱們暫時拋開兩個概念,咱們先來講下 Python 中的賦值,以個人理解,其實就是下定義的步驟,若是你們看過《武林外傳》中第二十九集《呂聖人智鬥姬無命 佟掌櫃火拼展紅綾》,就更容易理解如下的概念了學習

pic_1.png

賦值這個操做,其實能夠理解成給物體貼標籤,或者能夠理解爲給物體命名,既然是名稱,就像《武林外傳》中的同樣,你能夠叫「姬無命」,我也能夠叫「姬無命」。重要的是這個物體,而不是標籤。其中,這個標籤(或者名稱),咱們在計算機中把它叫作變量,物體就是實際的值ui

咱們知道,在計算機中,值會佔用必定的空間去存儲,而計算機爲了方便找到它,則會給它一個地址,方便咱們找到它

pic_2.png

咱們寫段代碼理解下

>>> zxj = "張小雞"
>>> jwm = zxj
>>> print(id(zxj))
4334207504
>>> print(id(jwm))
4334207504
複製代碼

pic_3.png

看圖理解很簡單,這裏的物體是字符「張小雞」,咱們把它貼上標籤「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
複製代碼

pic_4.png

咱們在賦值時,其實就至關於把函數的形參b這個標籤又貼在了「張小雞」物體上。後面咱們再執行b=「姬無命」時,就至關於把b這個標籤從「張小雞」這個物體上撕下來,放到「姬無命」這個物體上

可變和不可變對象

Python 內部對對象進行了區分,即爲可變對象和不可變對象,類型以下

int、str、float、tuple等爲不可變對象

list、dict、set等爲可變對象

不可變對象咱們上面已經說過他的賦值的特色,咱們這裏主要看可變對象。對於可變對象,咱們能夠簡單的理解爲作了個包裝盒。咱們在賦值的時候,這個標籤是貼在了這個包裝盒子上。計算機會記錄這個盒子的地址,裏面每個物體的地址,計算機也仍然會記錄

pic_5.png

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」,自始至終,由於咱們沒有動過盒子自己,因此他的地址不會發生變化

pic_6.png

結合上圖來看下,咱們修改一下代碼,再深刻看下盒子和盒子裏面物體的地址的變化

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可變和不可變對象

相關文章
相關標籤/搜索