1. 程式人生 > >python 中的可變對象與不可變對象

python 中的可變對象與不可變對象

parent ict 語句 圖片 代碼 最好 turn str 我們

近日辭職待工,沒有實際的項目與大家分享。暫寫寫在實際運用python中遇到的關於可變對象和不可變對象的坑。

首先我們需要明確一個概念,在python中一且皆對象。我們一般定義一個變量a=0,其實質a是一個類型變量,python 會把a封裝為一個pyObject。我後面會用type方法來說明這個問題。

說在前面,在python 中變量名是一個類似標簽的東西。它之前是什麽類型與它之後是什麽類型完全沒有關系。我們可以把它理解為一個貼紙,可以隨意從區塊A撕下來貼到區塊B上。正因為有這個特點,我們在使用python 對變量賦值時就無需擔心變量類型問題。當然,為了規範和後面代碼維護方便,此種做法需要節制,不然會讓自己都搞不清最終需要的對象到底個什麽類型,不方便調程序。

不可變對象

不可變對象一般包括int, float, bool, str, tuple. 不可變對象在函數調用過程中不會被更改,除非重新賦值。

技術分享圖片

這個例子中,a的類型為 int 說明它是一個類實例對象,根據python官方文檔https://docs.python.org/3/library/functions.html#int,顯示 class int ,進一步證實我們開篇提到的問題。 我們看到將變量a傳入函數addTwoNum 中,將a+b的值賦給a並返回。從結果上看a的值並沒有發生改變。我們在來看看各個變量的id。

技術分享圖片

技術分享圖片

這裏我們看到,a 和 c 的id 不一樣,說明它們是兩個獨立的存儲區域。我們在看另一種寫法。

技術分享圖片

這裏我們看到,a 的id發生了變化,而且巧合的是現在的a 的id和 c 一樣。說明此時a 的指向已經發生了改變。通過這個例子我們可以下這麽個結論:不可變對象的存儲區域相互獨立。每次產生以個新的不可變對象則重新開辟一塊區域。

可變對象

可變對象主要包括:list, dict, set。可變對象在參數傳遞過程會被改變。下面給出一個簡單的例子:

技術分享圖片

我們看到,函數的返回類型什麽也沒有,但是li 列表的值確實發生了變化,我們還觀察到一個很有意思的事,li 中元素的id 值都順延了,不知道是不是由於li 中值時連續的整數的原因。為了證明li 的首部沒有變,及li 本身指向的區塊沒有變,我們加入打印li 的id 的語句

技術分享圖片

從圖上可以發現,li 的id 確實沒有改變。從而可以說明li 沒有變,但其值發生了變化。

運用

在知道了不可變對象和可變對象的特性後 ,我們可以根據它們各自的特點避免一些坑,特別是可變對象。在申明函數時,若函數參數存在可變變量,如果不需要每次都產生新的存儲空間,我們可以不用另加判斷,其中特別需要註意的函數中的局部變量一定不能與全局變量重名。如果需要每次都產生新的存儲空間,那麽最好是在函數內部聲明一個變量,然後最後返回。例如,第一種情況,不需要每次都產生新的存儲空間。

技術分享圖片

每執行一次函數,li 就增加兩個元素。這種特性,用在遞歸中有很好的效果。給出一個括號匹配的算法,其中就運用到了可變變量這個能不斷修改元素、添加元素的特性。

def generateParenthesis(N):
        ans = []
        def backtrack(S = ‘‘, left = 0, right = 0):
            if len(S) == 2 * N:
                ans.append(S)
                return
            if left < N:
                backtrack(S+(, left+1, right)
            if right < left:
                backtrack(S+), left, right+1)

        backtrack()
        return ans

技術分享圖片

第二種情況,每次都需要開辟新的存儲空間:

技術分享圖片

雖然不可變變量與可變變量是一個簡單的問題,我們也不能小看它呀,有時往往就是這種細小的錯誤讓人抓狂。簡單的一篇博文,有不足之處請及時指出,共同進步。

python 中的可變對象與不可變對象