一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Python里參數(shù)是值傳遞還是引用傳遞?

 excel05 2022-05-06 發(fā)布于福建省

Python就是把一些參數(shù)從一個函數(shù)傳遞到另一個函數(shù),從而使其執(zhí)行相應的任務。但是你有沒有想過,參數(shù)傳遞的底層是如何工作的,原理又是怎樣的呢?

實際工作中,很多人會遇到這樣的場景:寫完了代碼,一測試,發(fā)現(xiàn)結果和自己期望的不一樣,于是開始一層層地 debug。花了很多時間,可到最后才發(fā)現(xiàn),是傳參過程中數(shù)據(jù)結構的改變,導致了程序的“出錯”。

比如,我將一個列表作為參數(shù)傳入另一個函數(shù),期望列表在函數(shù)運行結束后不變,但是往往“事與愿違”,由于某些操作,它的值改變了,那就很有可能帶來后續(xù)程序一系列的錯誤。

因此,了解 Python 中參數(shù)的傳遞機制,具有十分重要的意義,這往往能讓我們寫代碼時少犯錯誤,提高效率。今天我們就一起來學習一下,Python 中參數(shù)是如何傳遞的。

什么是值傳遞和引用傳遞

如果你接觸過其他的編程語言,比如 C/C++,很容易想到,常見的參數(shù)傳遞有 2 種:值傳遞和引用傳遞。所謂值傳遞,通常就是拷貝參數(shù)的值,然后傳遞給函數(shù)里的新變量。這樣,原變量和新變量之間互相獨立,互不影響。比如,我們來看下面的一段 C++ 代碼:

#include <iostream>using namespace std; // 交換2個變量的值void swap(int x, int y) { int temp; temp = x; // 交換x和y的值 x = y; y = temp; return;}int main () { int a = 1; int b = 2; cout << 'Before swap, value of a :' << a << endl; cout << 'Before swap, value of b :' << b << endl; swap(a, b); cout << 'After swap, value of a :' << a << endl; cout << 'After swap, value of b :' << b << endl; return 0;}Before swap, value of a :1Before swap, value of b :2After swap, value of a :1After swap, value of b :2

這里的 swap() 函數(shù),把 a 和 b 的值拷貝給了 x 和 y,然后再交換 x 和 y 的值。這樣一來,x 和 y 的值發(fā)生了改變,但是 a 和 b 不受其影響,所以值不變。這種方式,就是我們所說的值傳遞。

所謂引用傳遞,通常是指把參數(shù)的引用傳給新的變量,這樣,原變量和新變量就會指向同一塊內(nèi)存地址。如果改變了其中任何一個變量的值,那么另外一個變量也會相應地隨之改變。

還是拿我們剛剛講到的 C++ 代碼為例,上述例子中的 swap() 函數(shù),如果改成下面的形式,聲明引用類型的參數(shù)變量:

void swap(int& x, int& y) {   int temp;   temp = x; // 交換x和y的值   x = y;   y = temp;   return;}

那么輸出的便是另一個結果:

Before swap, value of a :1Before swap, value of b :2After swap, value of a :2After swap, value of b :1

原變量 a 和 b 的值被交換了,因為引用傳遞使得 a 和 x,b 和 y 一模一樣,對 x 和 y 的任何改變必然導致了 a 和 b 的相應改變。

不過,這是 C/C++ 語言中的特點。那么 Python 中,參數(shù)傳遞到底是如何進行的呢?它們到底屬于值傳遞、引用傳遞,還是其他呢?

在回答這個問題之前,讓我們先來了解一下,Python 變量和賦值的基本原理。

a = 1b = aa = a + 1

這里首先將 1 賦值于 a,即 a 指向了 1 這個對象,如下面的流程圖所示:

文章圖片1

接著 b = a 則表示,讓變量 b 也同時指向 1 這個對象。這里要注意,Python 里的對象可以被多個變量所指向或引用。

文章圖片2

最后執(zhí)行 a = a + 1。需要注意的是,Python 的數(shù)據(jù)類型,例如整型(int)、字符串(string)等等,是不可變的。所以,a = a + 1,并不是讓 a 的值增加 1,而是表示重新創(chuàng)建了一個新的值為 2 的對象,并讓 a 指向它。但是 b 仍然不變,仍然指向 1 這個對象。

因此,最后的結果是,a 的值變成了 2,而 b 的值不變?nèi)匀皇?1。

文章圖片3

通過這個例子你可以看到,這里的 a 和 b,開始只是兩個指向同一個對象的變量而已,或者你也可以把它們想象成同一個對象的兩個名字。簡單的賦值 b = a,并不表示重新創(chuàng)建了新對象,只是讓同一個對象被多個變量指向或引用。

同時,指向同一個對象,也并不意味著兩個變量就被綁定到了一起。如果你給其中一個變量重新賦值,并不會影響其他變量的值。

明白了這個基本的變量賦值例子,我們再來看一個列表的例子:

l1 = [1, 2, 3]l2 = l1l1.append(4)l1[1, 2, 3, 4]l2[1, 2, 3, 4]

同樣的,我們首先讓列表 l1 和 l2 同時指向了[1, 2, 3]這個對象。

文章圖片4

由于列表是可變的,所以 l1.append(4) 不會創(chuàng)建新的列表,只是在原列表的末尾插入了元素 4,變成[1, 2, 3, 4]。由于 l1 和 l2 同時指向這個列表,所以列表的變化會同時反映在 l1 和 l2 這兩個變量上,那么,l1 和 l2 的值就同時變?yōu)榱薣1, 2, 3, 4]。

文章圖片5

另外,需要注意的是,Python 里的變量可以被刪除,但是對象無法被刪除。比如下面的代碼:

arr = [1, 2, 3]del arr

del arr 刪除了 arr 這個變量,從此以后你無法訪問 arr,但是對象[1, 2, 3]仍然存在。Python 程序運行時,其自帶的垃圾回收系統(tǒng)會跟蹤每個對象的引用。如果[1, 2, 3]除了 arr 外,還在其他地方被引用,那就不會被回收,反之則會被回收。

由此可見,在 Python 中:

1、變量的賦值,只是表示讓變量指向了某個對象,并不表示拷貝對象給變量;而一個對象,可以被多個變量所指向。

2、可變對象(列表,字典,集合等等)的改變,會影響所有指向該對象的變量。

3、對于不可變對象(字符串、整型、元組等等),所有指向該對象的變量的值總是一樣的,也不會改變。但是通過某些操作(+= 等等)更新不可變對象的值時,會返回一個新的對象。

4、變量可以被刪除,但是對象無法被刪除。

Python 函數(shù)的參數(shù)傳遞

從上述 Python 變量的命名與賦值的原理講解中,相信你能舉一反三,大概猜出 Python 函數(shù)中參數(shù)是如何傳遞了吧?

這里首先引用 Python 官方文檔中的一段說明:

“Remember that arguments are passed by assignment in Python. 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.”

準確地說,Python 的參數(shù)傳遞是賦值傳遞 (pass by assignment),或者叫作對象的引用傳遞(pass by object reference)。Python 里所有的數(shù)據(jù)類型都是對象,所以參數(shù)傳遞時,只是讓新變量與原變量指向相同的對象而已,并不存在值傳遞或是引用傳遞一說。比如,我們來看下面這個例子:

def my_func1(b): b = 2a = 1my_func1(a)a1

這里的參數(shù)傳遞,使變量 a 和 b 同時指向了 1 這個對象。但當我們執(zhí)行到 b = 2 時,系統(tǒng)會重新創(chuàng)建一個值為 2 的新對象,并讓 b 指向它;而 a 仍然指向 1 這個對象。所以,a 的值不變,仍然為 1。

那么對于上述例子的情況,是不是就沒有辦法改變 a 的值了呢?答案當然是否定的,我們只需稍作改變,讓函數(shù)返回新變量,賦給 a。這樣,a 就指向了一個新的值為 2 的對象,a 的值也因此變?yōu)?2。

def my_func2(b):  b = 2  return ba = 1a = my_func2(a)a2

不過,當可變對象當作參數(shù)傳入函數(shù)里的時候,改變可變對象的值,就會影響所有指向它的變量。比如下面的例子:

def my_func3(l2): l2.append(4)l1 = [1, 2, 3]my_func3(l1)l1[1, 2, 3, 4]

這里 l1 和 l2 先是同時指向值為[1, 2, 3]的列表。不過,由于列表可變,執(zhí)行 append() 函數(shù),對其末尾加入新元素 4 時,變量 l1 和 l2 的值也都隨之改變了。但是,下面這個例子,看似都是給列表增加了一個新元素,卻得到了明顯不同的結果。

def my_func4(l2):  l2 = l2 + [4]l1 = [1, 2, 3]my_func4(l1)l1[1, 2, 3]

為什么 l1 仍然是[1, 2, 3],而不是[1, 2, 3, 4]呢?

要注意,這里 l2 = l2 + [4],表示創(chuàng)建了一個“末尾加入元素 4“的新列表,并讓 l2 指向這個新的對象。這個過程與 l1 無關,因此 l1 的值不變。當然,同樣的,如果要改變 l1 的值,我們就得讓上述函數(shù)返回一個新列表,再賦予 l1 即可:

def my_func5(l2): l2 = l2 + [4] return l2l1 = [1, 2, 3]l1 = my_func5(l1)l1[1, 2, 3, 4]

這里你尤其要記住的是,改變變量和重新賦值的區(qū)別:

1、my_func3() 中單純地改變了對象的值,因此函數(shù)返回后,所有指向該對象的變量都會被改變;

2、但 my_func4() 中則創(chuàng)建了新的對象,并賦值給一個本地變量,因此原變量仍然不變。

至于 my_func3() 和 my_func5() 的用法,兩者雖然寫法不同,但實現(xiàn)的功能一致。不過,在實際工作應用中,我們往往傾向于類似 my_func5() 的寫法,添加返回語句。這樣更簡潔明了,不易出錯。

總結

今天,我們一起學習了 Python 的變量及其賦值的基本原理,并且解釋了 Python 中參數(shù)是如何傳遞的。和其他語言不同的是,Python 中參數(shù)的傳遞既不是值傳遞,也不是引用傳遞,而是賦值傳遞,或者是叫對象的引用傳遞。

需要注意的是,這里的賦值或對象的引用傳遞,不是指向一個具體的內(nèi)存地址,而是指向一個具體的對象。

1、如果對象是可變的,當其改變時,所有指向這個對象的變量都會改變。

2、如果對象不可變,簡單的賦值只能改變其中一個變量的值,其余變量則不受影響。

清楚了這一點,如果你想通過一個函數(shù)來改變某個變量的值,通常有兩種方法。一種是直接將可變數(shù)據(jù)類型(比如列表,字典,集合)當作參數(shù)傳入,直接在其上修改;第二種則是創(chuàng)建一個新變量,來保存修改后的值,然后將其返回給原變量。在實際工作中,我們更傾向于使用后者,因為其表達清晰明了,不易出錯。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美又黑又粗大又硬又爽| 国产精品欧美激情在线观看| 儿媳妇的诱惑中文字幕| 国产盗摄精品一区二区视频| 色婷婷成人精品综合一区| 亚洲欧洲成人精品香蕉网| 精品欧美日韩一二三区| 日韩精品一区二区不卡| 91插插插外国一区二区婷婷| 欧美性猛交内射老熟妇| 国产一区二区不卡在线视频| 色综合久久六月婷婷中文字幕 | 免费人妻精品一区二区三区久久久 | 国产精品白丝久久av| 永久福利盒子日韩日韩| 欧美日韩国内一区二区| 亚洲国产精品肉丝袜久久| 大香蕉网国产在线观看av| 深夜少妇一区二区三区| 色涩一区二区三区四区| 九九热精彩视频在线免费| 日韩中文字幕人妻精品| 欧美一本在线免费观看| 亚洲精品福利视频在线观看| 亚洲精品有码中文字幕在线观看| 国产精品99一区二区三区| 国产毛片对白精品看片| 亚洲欧美日韩精品永久| 殴美女美女大码性淫生活在线播放 | 国产欧美日产中文一区| 一区二区三区亚洲天堂| 人妻熟女中文字幕在线| 欧美一区日韩二区亚洲三区| 久久黄片免费播放大全| 蜜臀人妻一区二区三区| 老司机精品视频免费入口| 国产成人午夜av一区二区| 欧美亚洲91在线视频| 日韩精品第一区二区三区| 欧美色婷婷综合狠狠爱| 最新国产欧美精品91|