Python中的變數 變數的定義 程式中,數據都臨時存儲在記憶體中。每一個被存儲在記憶體的數據都有一個記憶體地址。其中特定的數據被我們所使用,因此我們為那些記憶體地址定義了名稱。這一名稱被稱作 標識符,又稱變數名。而與變數名對應記憶體地址中的數據被稱為變數值。 總結:變數為記憶體中特定的數據。它的記憶體地址的名稱 ...
Python中的變數
變數的定義
程式中,數據都臨時存儲在記憶體中。
每一個被存儲在記憶體的數據都有一個記憶體地址。
其中特定的數據被我們所使用,因此我們為那些記憶體地址定義了名稱。
這一名稱被稱作 標識符,又稱變數名。
而與變數名對應記憶體地址中的數據被稱為變數值。
總結:變數為記憶體中特定的數據。它的記憶體地址的名稱為變數名,它的值為變數值。
在Python中,查看變數記憶體地址的方式為:id()
。
如:
>>>a = 1
>>>id(a)
140718160995112
變數的賦值方法
賦值:定義變數。
在Python里用等號=
來給變數賦值。
如:
>>>name = '總之先找時光機!'
>>>name
'總之先找時光機!'
>>>id(name)
2742705886128
其中name
為變數名,'總之先找時光機!'
是變數值。這個數據被存儲在地址:2742705886128
中。
變數名的命名規範
標識符(又稱變數名)命名規則是Python中定義各種名字的時候的統⼀規範,具體如下:
規則:
- 由數字、字母、下劃線組成
註意:
- 不能用數字開頭或只用數字
- 不能用Python內置的關鍵字或類型
- Python區分變數名大小寫
>>>import keyword
>>>print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
除了基礎的規則之外,還有其他約定俗稱的命名法。不過不同的單位與公司都有自己的編程規範,在此只介紹最基礎的四種命名法:駝峰命名法、帕斯卡命名法、下劃線命名法、匈牙利命名法。
駝峰命名法
又稱小駝峰式命名法。該命名規範,要求第一個單詞首字母小寫,後面其他單詞首字母大寫。
如:
>>>myName = '總之先找時光機!'
帕斯卡命名法
又稱大駝峰式命名法。該命名規範,每個單詞的第一個字母都要大寫。
如:
>>>MyName = '總之先找時光機!'
下劃線命名法
該命名規範,要求單詞與單詞之間通過下劃線連接即可。
如:
>>>my_name = '總之先找時光機!'
匈牙利命名法
該命名規範,要求首碼字母用變數類型的縮寫,其餘部分用變數的英文或英文的縮寫,單詞第一個字母大寫。
如:
>>>sMyName = '總之先找時光機!' # 這一變數為 str 類型
考慮到Python內的類型轉換比較自由,不建議使用該命名法。
變數的數據類型介紹
在Python里,數據的類型如下:
查看數據類型的方法:type()
如:
>>>a = 1
>>>type(a)
<class 'int'>
>>>b = 1.1
>>>type(b)
<class 'float'>
>>>c = True
>>>type(c)
<class 'bool'>
>>>d = '12345'
>>>type(d)
<class 'str'>
>>>e = [10, 20, 30]
>>>type(e)
<class 'list'>
>>>f = (10, 20, 30)
>>>type(f)
<class 'tuple'>
>>>h = {10, 20, 30}
>>>type(h)
<class 'set'>
>>>g = {'name': 'TOM', 'age': 20}
>>>type(g)
<class 'dict'>
Python中的複雜賦值與深淺拷貝
不可變類型
在Python中首先定義一個變數a = 1
。
>>>a = 1
>>>id(a)
140718136222504
此時一個數值1
被存儲在了記憶體空間,它所存儲的記憶體地址為140718136222504
。對應該地址的標識符(變數名)為a
。
如果再創建一個變數b = a
,會發生什麼呢?
>>>a = 1
>>>id(a)
140718136222504
>>>b = a
>>>id(b)
140718136222504
>>>b
1
使用id(b)
查驗該標識符對應的記憶體地址即可發現:標識符a和標識符b對應的是同一個記憶體地址。
這意味這Python並沒有重覆在記憶體空間記憶體儲數值1
,只是將標識符b與存儲數值1
的記憶體地址對應起來而已。
也就是說,現在記憶體地址140718136222504
現在有兩個名稱,一個是標識符a
,另一個是標識符b
。
當我們對變數a
,b
分別進行操作並改變它們的變數值時,他們的存儲地址和變數值又會發生什麼改變呢?
>>>a = 1
>>>b = a
>>>id(a)
140718136222504
>>>id(b)
140718136222504
>>>a = a + 1
>>>a
2
>>>id(a)
140718136222536
>>>b = b - 1
>>>b
0
>>>id(b)
140718136222472
在我們對變數a
和變數b
進行加減操作後,存儲變數a
、b
的記憶體地址改變了,而不是記憶體地址內的值改變了。
如果我們查驗數值0,1,2
的存儲地址,就會發現它們與變數值為0,1,2
的變數享有同樣的存儲地址:
>>>id(0)
140718136222472
>>>id(1)
140718136222504
>>>id(2)
140718136222536
對於這類改變變數值會改變記憶體地址的變數類型,我們稱為不可變類型。
所有數值類型、序列類型中的字元串和元組都屬於不可變類型。它們的記憶體地址隨著值的變化而變化。
可變類型
與不可變類型相對的,就是可變類型。包括列表、集合與字典。
可變類型變數的記憶體地址不會隨著值的變化而改變。
現在先定義一個可變類型變數list1 = [1, 2, 3, 4]
>>>list1 = [1, 2, 3, 4] # 這裡以list舉例
>>>id(list1)
2185105682944
>>>list1[3] = 5
>>>list1
[1, 2, 3, 5]
>>>id(list1)
2185105682944
可以發現當我改變了list1
內的元素,變數的記憶體地址並未發生改變。
現在再創建一個list2 = list1
,並再次嘗試改變變數list2
的值。
>>>list2 = list1
>>>id(list1)
2185105682944
>>>id(list2)
2185105682944
>>>list2[0] = 6 # 改變list2中第0元素的值
>>>list1 # list1也發生改變了
[6, 2, 3, 5]
>>>list2
[6, 2, 3, 5]
在我們創建list2 = list1
後,再查驗變數list1
和list2
的記憶體地址,發現它們是一樣的。
並且當我們改變變數list2
的內容,變數list1
的內容也改變了。
原因很簡單,因為標識符list1
和list2
指向的是同一個記憶體地址:我們通過標識符list2
去改變記憶體地址內的數據後,用標識符list1
查驗了同一個記憶體地址內的數據。
copy
module
偶爾,我們希望保留部分或者全部原始數據。Python中的copy
標準庫提供瞭解決方案。
copy
標準庫的交互
copy.copy(x)
Return a shallow copy of x. 返回一個淺拷貝。copy.deepcopy(x)
Return a deep copy of x. 返回一個深拷貝。
深淺拷貝的區別
當拷貝的對象是複合對象(即對象中包含其他對象,如列表中包含另一個列表)時:
- 淺拷貝創建一個新的複合對象,但內部的可變類型的記憶體地址被繼承。
- 深拷貝創建一個新的複合對象,且內部的可變累型也會被遞歸拷貝。
>>>import copy
>>>list1 = [1, [2, 3], 4, 5]
>>>list2 = copy.copy(list1)
>>>list3 = copy.deepcopy(list1)
>>>id(list1)
1754106448256
>>>id(list2)
1754106449216
>>>id(list3)
1754103446080
上述代碼創建了三個變數。
其中list1[1]
為嵌套的list
類型。list2
是list1
的淺拷貝,list3
是list1
的深拷貝。
此時標識符list1
、list2
和list3
各自對應不同的記憶體地址。
因為三個變數擁有不同的記憶體地址,所以我們直接對單個變數進行操作,這樣的改變不會影響其他變數。
如:
>>>list1
[1, [2, 3], 4, 5]
>>>list2
[1, [2, 3], 4, 5]
>>>list3
[1, [2, 3], 4, 5]
>>>list1.append(6)
>>>list2.append(7)
>>>list3.append(8)
>>>list1
[1, [2, 3], 4, 5, 6]
>>>list2
[1, [2, 3], 4, 5, 7]
>>>list3
[1, [2, 3], 4, 5, 8]
但如果我們檢查各個變數內嵌套的可變類型元素的記憶體地址,就會發現深淺層拷貝的區別:
id(list1[1])
1754106220480
id(list2[1])
1754106220480
id(list3[1])
1754106448960
我們發現,淺拷貝(list2
)中嵌套的list
元素與正本(list1
)中的它享有同樣的記憶體地址,而深拷貝(list3
)中的它則擁有不同的記憶體地址。
因此,如果我們對淺拷貝副本中的可變類型元素做出改變,我們期待正本中的可變類型元素也會對應地發生改變,因為它們擁有同樣的記憶體地址。
>>>list2[1].append(9)
>>>list1
[1, [2, 3, 9], 4, 5, 6]
>>>list2
[1, [2, 3, 9], 4, 5, 7]
>>>list3
[1, [2, 3], 4, 5, 8]