【Python】python動態類型
在python中,省去了變量聲明的過程,在引用變量時,往往一個簡單的賦值語句就同時完成了,聲明變量類型,變量定義和關聯的過程,那麽python的變量到底是怎樣完成定義的呢?
動態類型
python使用動態類型和他提供的多態性來提供python語言的簡潔靈活的基礎。在python中我們是不會聲明所使用對象的確切類型的。所謂的python動態類型,就是在程序運行的過程中自動決定對象的類型。
對象、變量和引用
當我們在賦值一個變量時,在python中其實自動做了很多事情。
1.創建變量:當代碼第一次賦值給一個變量時就創建了這個變量,在之後的賦值過程關聯值,python在代碼運行之前先檢驗變量名,可以當成是最初的賦值創建變量。
2.變量聲明:python中類型只存在於對象中,而不是變量,變量是通用的,他只是在程序的某一段時間引用了某種類型的對象而已,比如定義a =1 ,a = ‘a‘,一開始定義了變量a為指向了整型的對象,然後變量又指向了字符串類型的變量,可見,變量是不固定的類型。
3.變量使用:變量出現在表達式中就會馬上被對象所取代,無論對象是什麽內類型,變量在使用前必須要先定義。
值得註意的是,變量必須在初始化名字之後才能更新他們,比如計數器初始化為0,然後才能增加他。
也就是說,當我們給變量賦值的時候,比如a=3,python執行三個不同操作去完成賦值。
1.創建一個對象代表3,
2.如果程序中沒有變量a,則創建他。
3.將變量與對象3連接起來。
變量與對象是連接關系,它們存儲在內存的不同位置,如果有列表嵌套這樣大的對象,對象還連接到它包含的對象。這種從變量到對象的連接稱為引用。
變量的引用以內存中的指針形式實現。一旦變量被使用,那麽python自動跟變量的對象連接。具體來說:
1.變量是系統表的元素,他指向對象存放的地址空間。
2.對象是分配的一塊內存,地址可被連接,有足夠大空間代表對象的值,
3.引用的過程自動完成變量指向對象地址的過程,即從變量到對象的指針。
對象的垃圾回收
每個對象都有兩個標準頭部信息,一個是類型標誌符,用於標記對象類型,另一個是引用計數器,用來決定是不是可回收對象。很顯然,在python中只有對象才有類別區分,變量不過是引用了對象,變量並不具有類別區分,他只是在特定時間引用某個特定對象。
對於引用計數器的使用,則關聯到python的垃圾回收機制,當當一個變量名賦予了一個新的對象,那麽之前舊的對象占用的地址空間就會被回收。舊對象的空間自動放入內存空間池,等待後來的對象使用。
計數器在垃圾回收的過程中有事如何工作的呢?計數器記錄的是當前指向對象的引用數目,如果在某時刻計數器設置為0,則表示未被引用,name這個對象的內存空間就會收回。
對象的垃圾回收有著很大的意義,這使得我們在python中任意使用對象而且不需要考慮釋放空間,省去了C與C++中大量的基礎代碼。
共享引用(深淺拷貝的緣由)
當一個變量使用多個對象時,舊的對象會被垃圾收回,那麽變量共享變量的對象有事一種什麽樣的類型呢?
a=‘hello world’ b=a print(b) 運行結果: hello world
值得一提的是,這裏b變量引用的a作為值,根據python中賦值是以對象來完成的,所以b引用的應該是a變量指向的對象地址的值,故可以判斷,改變a指向的對象並不會影響b的值。
a=‘hello world’ b=a a=‘new hello world‘ print(a) print(b) 運行結果: new hello world hello world
python中變量總是一個指定對象的指針,而不是能夠改變內存區域的標簽,即給一個變量賦新的值,不是替換一個對象原始值,而是創建一個新得對象供變量引用。
當然這條只限於對象的類型不可改變,如果引用對象是像列表一樣可供修改的對象那結果如何呢?
a=[1,2,3] b=a a.append(4) print(a) print(b) 運行結果: [1, 2, 3, 4] [1, 2, 3, 4]
結果很顯然,對於可變類型對象,變量不會創建一個顯得對象,而是沿用之前的對象,即使對象已經被改變了。可以簡單的理解為,兩個對象同時指向了一個列表的內存地址,而列表又映射了裏面各元素的內存地址,變量的共享並不關註列表的改變,他們只關心列表的內存空間是否改變,所以,可變對象在引用時自身可以改變,所以不需要創建新的對象,所以共享對象會隨之前對象的變化而變化。
這其實是我們不希望看到的。我們可以使用拷貝對象創建引用:
a=[1,2,3] b=a[:] a.append(4) print(a) print(b) 運行結果: [1, 2, 3, 4] [1, 2, 3]
這種方法並不適用於不可索引但是可變的字典與集合,所以python的copy模塊用於變量引用:
import copy a=[1,2,3,[1,2]] b=copy.copy(a) c=copy.deepcopy(a) d=a a.append(4) a[3][0]=5 print(a) print(b) print(c) print(d) 運行結果: [1, 2, 3, [5, 2], 4] [1, 2, 3, [5, 2]] [1, 2, 3, [1, 2]] [1, 2, 3, [5, 2], 4]
共享引用的補充
其實是關於垃圾回收的一點補充,對於一些小的整數或字符串,並不像我們說的那樣計數器標記為0就被收回。這和python的緩存機制有關。對於一般的對象,python適用於垃圾收回。
a=[1,2,3] b=[1,2,3] c=a print(a == c) print(a == b) print(a is c) print(a is b) 運行結果: True True True False
對於小的整數與字符串則不同:
a=111 b=111 c=a print(a == c) print(a == b) print(a is c) print(a is b) 運行結果: True True True True
在python中,任何東西都是在賦值與引用中工作的,對於理解python動態類型,在以後的工作與學習時是有很大幫助的,這是python唯一的賦值模型,所以準確的理解與應用十分有必要。不對類型作約束,這使得python代碼極為靈活,高效,並且省去了大量的代碼,極為簡潔,所以python是這樣一門有藝術的編程。
【Python】python動態類型