溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

為什么Python中的函數(shù)會修改全局的列表和字典

發(fā)布時(shí)間:2020-07-15 17:17:21 來源:網(wǎng)絡(luò) 閱讀:754 作者:Python熱愛者 欄目:編程語言

Python中的函數(shù)(內(nèi)置函數(shù)和我們自己編寫的自定義函數(shù))是處理數(shù)據(jù)的關(guān)鍵工具。但是他們對我們的數(shù)據(jù)做了什么可能有點(diǎn)令人困惑,如果我們不知道發(fā)生了什么,它可能會在我們的分析中造成嚴(yán)重的錯(cuò)誤。

在本教程中,我們將詳細(xì)研究Python在函數(shù)中處理不同數(shù)據(jù)類型時(shí)是如何對它們進(jìn)行操作的,并學(xué)習(xí)如何確保只有在希望更改數(shù)據(jù)時(shí)才更改數(shù)據(jù)。

函數(shù)中的內(nèi)存隔離

為了理解Python如何處理函數(shù)內(nèi)部的全局變量,我們來做一個(gè)小實(shí)驗(yàn)。我們將創(chuàng)建兩個(gè)全局變量number_1和number_2,并為它們賦值整數(shù)?5?和?10。然后,我們將使用這些全局變量作為函數(shù)的參數(shù)來執(zhí)行一些簡單的數(shù)學(xué)運(yùn)算。我們還將使用變量名作為函數(shù)的參數(shù)名。然后,我們將查看函數(shù)中所有變量的使用是否影響了這些變量的全局值。

為什么Python中的函數(shù)會修改全局的列表和字典

正如我們在上面看到的,函數(shù)正常運(yùn)行,全局變量number_1和number_2的值沒有變化,盡管我們在函數(shù)中使用它們作為形參和實(shí)參名。這是因?yàn)镻ython將函數(shù)中的變量存儲在與全局變量不同的內(nèi)存位置。它們是被隔離的。因此,變量number_1可以在全局中有一個(gè)值(5),而在函數(shù)內(nèi)部有一個(gè)不同的值(50),在這個(gè)函數(shù)中它是獨(dú)立的。

(順便說一句,如果你對parameters(形參) 和 arguments(實(shí)參)之間的區(qū)別感到困惑,Python文檔中關(guān)于這個(gè)主題的內(nèi)容非常有用。)

那么列表和字典呢?

列表

我們已經(jīng)看到,我們在函數(shù)內(nèi)部對上面的number_1這樣的變量所做的操作并不影響它的全局值。但是number_1是一個(gè)整數(shù),這是一個(gè)非?;镜臄?shù)據(jù)類型。如果我們用不同的數(shù)據(jù)類型(比如列表)嘗試相同的實(shí)驗(yàn),會發(fā)生什么?下面,我們將創(chuàng)建一個(gè)名為duplicate_last()?的函數(shù),它將復(fù)制我們作為參數(shù)傳遞的任何列表中的最終條目。

為什么Python中的函數(shù)會修改全局的列表和字典

正如我們所看到的,這里?initial_list?的全局值被更新了,即使它的值只在函數(shù)內(nèi)部更改!

字典

現(xiàn)在,我們來編寫一個(gè)函數(shù),該函數(shù)以一個(gè)字典作為參數(shù)來查看全局字典變量在函數(shù)中被操作時(shí)是否也會被修改。

為了看起來更直觀一點(diǎn),我們將使用Python基礎(chǔ)課程中使用的?AppleStore.csv?數(shù)據(jù)集中的數(shù)據(jù)(數(shù)據(jù)可以從這里下載)。

在下面的代碼片段中,我們從一個(gè)字典開始,它包含了數(shù)據(jù)集中各個(gè)年齡級別的應(yīng)用程序的數(shù)量(因此有4433個(gè)應(yīng)用程序的級別為“4+”,987個(gè)應(yīng)用程序的級別為“9+”,等等)。假設(shè)我們想計(jì)算每個(gè)年齡等級的百分比,這樣我們就可以得到在App Store中哪個(gè)年齡等級是最常見的。

為此,我們將編寫一個(gè)名為?make_percentages()?的函數(shù),該函數(shù)以一個(gè)字典作為參數(shù)并將計(jì)數(shù)轉(zhuǎn)換為百分比。我們需要從0開始計(jì)數(shù),然后遍歷字典中的每個(gè)值,將它們添加到計(jì)數(shù)中,這樣就得到了評級的總數(shù)。然后我們將再次遍歷字典,并對每個(gè)值做一些數(shù)學(xué)運(yùn)算來計(jì)算百分比。

為什么Python中的函數(shù)會修改全局的列表和字典

在查看輸出之前,讓我們快速回顧一下上面發(fā)生的事情。在將我們的app 年齡評級字典分配給變量content_ratings之后,我們創(chuàng)建了一個(gè)名為make_percentages()的新函數(shù),它只接受一個(gè)參數(shù):?a_dictionary。

為了計(jì)算每個(gè)年齡等級的應(yīng)用程序所占比例,我們需要知道應(yīng)用程序的總數(shù),因此我們首先將一個(gè)名為total的新變量設(shè)置為0,然后在a_dictionary中循環(huán)遍歷每個(gè)鍵值,并將其添加到total中。

完成之后,我們需要做的就是再次遍歷a_dictionary,將每個(gè)條目除以總數(shù),然后將結(jié)果乘以100。這將給我們返回一個(gè)包含百分比的詞典。

但是,當(dāng)我們使用全局content_ratings變量作為這個(gè)新函數(shù)的參數(shù)時(shí)發(fā)生了什么呢?

為什么Python中的函數(shù)會修改全局的列表和字典

正如我們在列表中看到的,我們的全局content_ratings變量已經(jīng)更改,盡管它只是在我們創(chuàng)建的make_percentages()函數(shù)中進(jìn)行了修改。

這里到底發(fā)生了什么?我們遇到了可變不可變數(shù)據(jù)類型之間的差異。

可變和不可變的數(shù)據(jù)類型

在Python中,數(shù)據(jù)類型可以是可變的(可更改的),也可以是不可變的(不可更改的)。雖然我們在介紹Python時(shí)使用的大多數(shù)數(shù)據(jù)類型都是不可變的(包括整數(shù)、浮點(diǎn)數(shù)、字符串、布爾值和元組),但是列表和字典是可變的。這意味著全局列表或字典即使在函數(shù)內(nèi)部使用時(shí)也可以更改,就像我們在上面的示例中看到的那樣。

要理解可變(可更改)和不可變(不可更改)之間的區(qū)別,了解Python如何處理這些變量是很有幫助的。

讓我們從考慮一個(gè)簡單的變量賦值開始:

為什么Python中的函數(shù)會修改全局的列表和字典

變量名a的作用類似于一個(gè)指向5的指針,它可以幫助我們隨時(shí)檢索5。

5是一個(gè)整數(shù),整數(shù)是不可變的數(shù)據(jù)類型。如果數(shù)據(jù)類型是不可變的,這意味著一旦創(chuàng)建,就不能更新它。如果我們執(zhí)行a += 1,我們實(shí)際上并沒有更新5到6。在下面的動畫中,我們可以看到這一點(diǎn):

a?初始指向?5.

執(zhí)行a += 1?后, 將指針從?5指向?6, 它并沒有實(shí)際改變?5.

可變數(shù)據(jù)類型(如列表和字典)的行為有所不同。它們可以更新。舉個(gè)例子,我們來創(chuàng)建一個(gè)非常簡單的列表:

為什么Python中的函數(shù)會修改全局的列表和字典

如果我們在列表末尾添加一個(gè)3,我們不是簡單地將list_1指向另一個(gè)列表,而是直接更新現(xiàn)有列表:

即使我們創(chuàng)建多個(gè)列表變量,只要它們指向同一個(gè)列表,當(dāng)列表被更改時(shí),它們都會被更新,如下面的代碼所示:

為什么Python中的函數(shù)會修改全局的列表和字典

下面是上面代碼中實(shí)際發(fā)生的動態(tài)可視化:

這就解釋了為什么我們之前在試驗(yàn)列表和字典時(shí)我們的全局變量被改變了。因?yàn)榱斜砗妥值涫强勺兊?,所以更改它?即使是在函數(shù)中)也會更改列表或字典本身,這與不可變數(shù)據(jù)類型不同。

保持可變數(shù)據(jù)類型不變

一般來說,我們不希望函數(shù)更改全局變量,即使它們包含列表或字典之類的可變數(shù)據(jù)類型。這是因?yàn)樵诟鼜?fù)雜的分析和程序中,我們可能會經(jīng)常使用許多不同的函數(shù)。如果所有函數(shù)都更改它們正在調(diào)用的列表和字典,那么要跟蹤什么在更改什么就會變得非常困難。

幸運(yùn)的是,有一種簡單的方法可以繞過這個(gè)問題:我們可以使用內(nèi)建的Python方法.copy()復(fù)制列表或字典。

如果你還沒有學(xué)習(xí)過方法,請不要擔(dān)心。它們包含在我們的中級Python課程中,但是在本教程中,你只需要知道.copy()的工作原理類似于.append():

為什么Python中的函數(shù)會修改全局的列表和字典

讓我們再看一下我們?yōu)榱斜韺懙暮瘮?shù),對它進(jìn)行更新,這樣函數(shù)內(nèi)部執(zhí)行的操作就不會更改initial_list。我們只需要將傳遞給函數(shù)的參數(shù)從initial_list更改為initial_list.copy()

為什么Python中的函數(shù)會修改全局的列表和字典

正如我們所看到的,這已經(jīng)解決了我們的問題。原因如下:使用.copy()創(chuàng)建一個(gè)列表的獨(dú)立副本,這樣a_list就不會指向initial_list本身,而是指向一個(gè)以initial_list副本開始的新列表。在此之后對a_list所做的任何更改都將只對該獨(dú)立列表生效,而不是initial_list本身,因此initial_list的全局值將保持不變。

不過,這個(gè)解決方案仍然不完美,因?yàn)槊看蜗蚝瘮?shù)傳遞參數(shù)時(shí)都必須記得添加.copy(),否則可能會意外更改initial_list的全局值。如果我們不想操心這個(gè),我們可以在函數(shù)內(nèi)部創(chuàng)建列表拷貝:

為什么Python中的函數(shù)會修改全局的列表和字典

使用這種方法,我們可以安全地將一個(gè)可變的全局變量(如initial_list)傳遞給我們的函數(shù),全局值不會被改變,因?yàn)楹瘮?shù)本身會復(fù)制一個(gè)副本,然后對該副本執(zhí)行操作。

.copy()方法也適用于字典。與列表一樣,我們可以簡單地將.copy()添加到傳遞給函數(shù)的參數(shù)中,創(chuàng)建一個(gè)用于函數(shù)的拷貝,而不會改變原始變量:

為什么Python中的函數(shù)會修改全局的列表和字典

但是,再次說明,使用這種方法意味著在每次將字典傳遞給make_percentages()函數(shù)時(shí),都要記得添加.copy()。如果我們要頻繁地使用這個(gè)函數(shù),最好在函數(shù)內(nèi)部實(shí)現(xiàn)復(fù)制,這樣我們就不需要記住了。

下面,我們將在函數(shù)內(nèi)部使用.copy()。這樣,就可以確保我們在將全局變量作為參數(shù)傳遞給函數(shù)時(shí)不會被更改,而且我們也不需要記得為傳遞的每個(gè)參數(shù)添加.copy()。

為什么Python中的函數(shù)會修改全局的列表和字典

正如我們所看到的,修改我們的函數(shù)來創(chuàng)建字典的副本,然后只在副本中將計(jì)數(shù)更改為百分比,這樣我們就可以在不更改content_ratings的情況下執(zhí)行我們想要的操作。

結(jié)論

在本教程中,我們研究了可變數(shù)據(jù)類型(可以更改)和不可變數(shù)據(jù)類型(不能更改)之間的區(qū)別。我們學(xué)習(xí)了如何使用.copy()方法復(fù)制列表和字典等可變數(shù)據(jù)類型,這樣我們就可以在不更改其全局值的情況下在函數(shù)中使用它們。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI