成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

關(guān)于函數(shù)參數(shù)傳遞,80%人都錯了

X_AirDu / 1240人閱讀

摘要:另外說下,函數(shù)的返回值,也相當于是一次賦值。只不過,這時候是把函數(shù)內(nèi)部返回值所指向的對象,賦值給外面函數(shù)的調(diào)用者輸出函數(shù)結(jié)束后,這個標簽雖然不存在了,但所指向的對象依然存在,就是指向的新對象。

還記得上一次關(guān)于變量作用域文章 :

Crossin:全菊變量和菊部變量zhuanlan.zhihu.com

我們在公眾號(Crossin的編程教室)里做了個問題投票:


def func(m):
    m[0] = 20
    m = [4, 5, 6]
    return m

l = [1, 2, 3]
func(l)
print("l =", l)

實際的輸出我想大家都嘗試過了吧,應(yīng)該是選項二:
[20, 2, 3]

和80%人想象中的結(jié)果不一樣。

這是為什么呢?

在 Python 的官方文檔 FAQ 里有這樣一句話

Remember that arguments are passed by assignment in Python.  
要記住,Python 里的參數(shù)是通過賦值傳遞的。

https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

所以要弄清楚參數(shù)傳遞,先得弄清 Python 的賦值。

或許在很多人的直觀印象中,變量是一個容器;給變量賦值,就像是往一個存儲的容器中填入一個數(shù)據(jù);再次賦值就是把容器中的數(shù)據(jù)換掉。

然而,

在 Python 中,這種理解是不準確的!
在 Python 中,這種理解是不準確的!
在 Python 中,這種理解是不準確的!

若是想要個形象的類比, Python 中的變量更像是是個標簽;給變量賦值,就是把標簽貼在一個物體上;再次賦值就是把標簽貼在另一個物體上

體會下這兩種設(shè)計的差異:

· 前者,變量是一個固定的存在,賦值只會改變其中的數(shù)值,而變量本身沒有改動。
· 后者,變量不存在實體,它僅僅是一個標簽,一旦賦值就被設(shè)置到另一個物體上,不變的是那些物體。

這些“物體”就是 對象 。 Python 中所有東西都是對象 ,包括函數(shù)、類、模塊,甚至是字符串’hello’,數(shù)字1、2、3,都是對象。

用個例子來說明:


a = 1
b = 2
c = 1
# 再次賦值
a = b

在這個代碼里,a 和 c 其實指向的是同一個對象—整數(shù) 1。給 a 賦值為 b 之后,a 就變成了指向 2 的標簽,但 1 和 c 都不會受影響。

示意圖:

更有說服力一點的驗證:


a = 1
print("a", a, id(a))
b = 2
print("b", b, id(b))
c = 1
print("c", c, id(c))
# 再次賦值
a = b
print("a", a, id(a))

輸出:


a 1 4301490544
b 2 4301490576
c 1 4301490544
a 2 4301490576

id() 可以認為是獲取一個對象的地址??梢钥闯?,a 和 c 開始其實是同一個地址,而后來賦值之后,a 又和 b 是同一個地址。

每次給變量重新賦值,它就指向了新的地址,與原來的地址無關(guān)了。

回到函數(shù)的調(diào)用上:
Python 里的參數(shù)是通過賦值傳遞的


 def fn(x):
    x = 3

a = 1
fn(a)
print(a)

輸出結(jié)果為 1,a 沒有變化。

調(diào)用 fn(a) 的時候,就相當于做了一次 x = a,把 a 賦值給了 x,也就是把 x 這個標簽貼在了 a 的對象上。只不過 x 的作用域僅限于函數(shù) fn 內(nèi)部。

當 x 在函數(shù)內(nèi)部又被賦值為 3 時,就是把 x 又貼在了 3 這個對象上,與之前的 a 不在有關(guān)系。所以外部的 a 不會有任何變化。

把其中的數(shù)值換成其他對象,效果也是一樣的:


def fn(x):
    x = [4,5,6]

a = [1,2,3]
fn(a)
print(a)

輸出結(jié)果為 [1,2,3],a 沒有變化。(記住這個例子,最后我們還會提到)

那上次的題目又是怎么回事?

我們再來看一個賦值:


a = [1,2,3]
print("a", a, id(a))
b = a
print("b", b, id(b))
b[1] = 5
print("a", a, id(a))
print("b", b, id(b))

輸出:


a [1, 2, 3] 4490723464
b [1, 2, 3] 4490723464
a [1, 5, 3] 4490723464
b [1, 5, 3] 4490723464

這個是不是好理解一點?b 賦值為 a 后,和 a 指向同一個列表對象。[1] 這個基于 index 的賦值是 list 對象本身的一種操作,并沒有給 b 重新貼標簽,改變的是對象本身。所以 b 指向的還是原來的對象,此對象的改動自然也會體現(xiàn)在 a 身上。同理,b.append(7) 這樣的操作也會是類似的效果。

再來回顧下原問題呢:


def func(m):
    m[0] = 20
    # m = [4, 5, 6]
    return m

l = [1, 2, 3]
func(l)
print("l =", l)

去掉那句 m=[4,5,6] 的干擾,函數(shù)的調(diào)用就相當于:


l = [1, 2, 3]
m = l
m[0] = 20

l 的值變成 [20,2,3] 沒毛病吧。而對 m 重新賦值之后,m 與 l 無關(guān),但不影響已經(jīng)做出的修改。

這就是這道題的解答。上次留言里有些同學(xué)已經(jīng)解釋的很準確了。

另外說下, 函數(shù)的返回值 return,也相當于是一次賦值 。只不過,這時候是把函數(shù)內(nèi)部返回值所指向的對象,賦值給外面函數(shù)的調(diào)用者:


def fn(x):
    x = 3
    print("x", x, id(x))
    return x

a = 1
a = fn(a)
print("a", a, id(a))

輸出:


x 3 4556777904
a 3 4556777904

函數(shù)結(jié)束后,x 這個標簽雖然不存在了,但 x 所指向的對象依然存在,就是 a 指向的新對象。

所以,如果你想要通過一個函數(shù)來修改外部變量的值,有幾種方法:

通過返回值賦值

使用全局變量

修改 list 或 dict 對象的內(nèi)部元素

修改類的成員變量

有相當多的教程把 Python 的函數(shù)參數(shù)傳遞分為可變對象和不可變對象(這個概念下次來說)來說明,然后類比到 C++ 的值傳遞和引用傳遞。我很反對這樣去理解:

對于沒有學(xué)過 C++ 的人來說,這個解釋屬于循環(huán)論證,還是沒說清問題。

Python 本來就不存在值傳遞/引用傳遞的概念,這個比較沒有意義。

這個類比實際上是錯誤的。就算類比,也應(yīng)該是相當于 C++ 里的指針值傳遞。

用可變對象/不可變對象來劃分很容易產(chǎn)生誤解,比如我們前面例子中的 x=[4,5,6],它是可變對象,但一樣不影響外部參數(shù)的值。

這點前面貼出的官方文檔里也直說了:

Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.  
賦值是創(chuàng)建了一份對象的引用(也就是地址),形參和實參之間不存在別名的關(guān)系,本質(zhì)上不存在引用傳遞。

網(wǎng)上很容易搜到“參數(shù)是可變對象就相當于引用傳遞”這種錯誤的理解。也不知道他們是對 Python 的參數(shù)傳遞有什么誤解,還是對C++的引用傳遞有什么誤解。結(jié)果就是,讓很多初學(xué)者從網(wǎng)上看了幾篇教程之后,更糊涂了。

所以呢,找到一個靠譜的教程是非常重要滴

════
其他文章及回答:

如何自學(xué)Python | 新手引導(dǎo) | 精選Python問答 | Python單詞表 | 區(qū)塊鏈 | 人工智能 | 雙11 | 嘻哈 | 爬蟲 | 排序算法 | 我用Python | 高考 | 世界杯

歡迎搜索及關(guān)注: Crossin的編程教室

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/42456.html

相關(guān)文章

  • 可變對象與不可變對象

    摘要:不可變對象不允許對自身內(nèi)容進行修改。因為他們說到不可變對象時用的是賦值,而說到可變對象又用了的索引等方法,這根本是兩碼事?;谶@一設(shè)定,兩者在功能上的最大區(qū)別就是不可變對象可以作為字典的鍵,而可變對象不行。 前陣子我們聊了下函數(shù)的參數(shù)傳遞以及變量賦值的一些內(nèi)容:關(guān)于函數(shù)參數(shù)傳遞,80%人都錯了 簡單回顧下要點: 1. Python 中的變量不是裝有對象的 容器 ,而是貼在對象上的 標簽...

    jay_tian 評論0 收藏0
  • python深拷貝與淺拷貝

    摘要:之前關(guān)于的作用域賦值參數(shù)傳遞,我們接連談了幾篇文章全菊變量和菊部變量關(guān)于函數(shù)參數(shù)傳遞,人都錯了可變對象與不可變對象今天我們依然要就相關(guān)話題繼續(xù)下去。這是由于它們是不可變對象,不存在被修改的可能,所以拷貝和賦值是一樣的。 之前關(guān)于 Python 的作用域、賦值、參數(shù)傳遞,我們接連談了幾篇文章: 全菊變量和菊部變量 關(guān)于函數(shù)參數(shù)傳遞,80%人都錯了 可變對象與不可變對象 今天我們依然要...

    ideaa 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<