python是動態類型的語言,不須要聲明變量的類型。php
實際上,python中的變量僅僅只是用來保存一個數據對象的地址。不管是什麼數據對象,在內存中建立好數據對象以後,都只是把它的地址保存到變量名中。因此變量名是類型無關的,但它指向的值是類型相關的,能夠是數值、字符串、列表、函數、類、對象等等。這些內存對象中都至少包含3部分:對象類型、對象的引用計數(用來判斷改對象是否可被垃圾回收器回收)、對象的值。python
所以,a = 3
中,變量名a保存的是數據對象3的地址,以後能夠爲其賦值一個字符串a = "hello"
,這時a保存的是"hello"字符串的地址。在這個類型改變的過程當中,a僅僅只是修改了一下地址而已。shell
python中的變量命名時只能包含數字、大小寫字母、下劃線這三種類型的字符,而且數字不能是首字符。app
還有一些有特殊意義的變量命名方式(目前這些內容瞭解便可):函數
__name__
,這種類型的變量在python中有特殊意義,屬於對象的內置屬性,之後學了類和對象就知道了_x
,這類變量不會被from ModuleName import *
的方式導入__x
,這類變量是類的本地變量或稱爲類的私有變量,它會擴展成__classname_x
除此以外,還有約定俗成的命名方式:性能
由於只是約定俗成,因此沒有強制限制。優化
本文解釋python中變量賦值的形式,並解釋一些細節。後面還有一篇文章解釋python中按引用賦值的文章。code
python中變量賦值的幾種形式。對象
x = "long" # (1).基本形式 x, y = "long", "shuai" # (2).元組對應賦值 [x, y] = ["long", "shuai"] # (3).列表對應賦值 a, b, c, d = "long" # (4).序列賦值 a, *b = 'long' # (5).解包賦值 a = b = "long" # (6).多目標賦值 a += 3 # (7).二元賦值表達式 ((a, b), c) = ('lo','ng') # (8).嵌套賦值序列
注意:python的數值是不可變對象,沒法在原處修改數據,因此不支持自增、自減。blog
a++ ++a a-- --b
其中(1)-(3)無需過多解釋,惟一須要注意的是,當使用逗號的時候,python總會臨時或永久地創建成tuple來保存元素,因此x, y = "long", "shuai"
在內部徹底等價於(x, y) = ("long", "shuai")
。
實際上,列表元素也能夠賦值給元組,或者元組賦值給列表,只要兩邊的序列元素個數能對應,無所謂左右兩邊的序列類型:
>>> (x,y) = (1,2) >>> (x,y) = [1,2] >>> [x,y] = (1,2) >>> [x,y] = [1,2] >>> (x,y) = 'ab' >>> [x,y] = 'ab'
對於(4)賦值方式,是序列賦值的行爲,在python中,只要是序列,均可以這樣賦值。正如這裏的變量賦值狀況等價於:
a = "l" b = "o" c = "n" d = "g"
若是換成其它的序列也同樣。例如:
a, b, c, d = ("shell","perl","php","python") a, b, c, d = ["shell","perl","php","python"]
可是變量和序列中的元素必須一一對應。若是變量名與元素個數不一樣,則會報錯,除非只有一個變量名,這表示將整個序列賦值給這個變量。
若是想要將序列中的元素賦值給不等的變量,能夠考慮先將序列進行切片。
>>> str='long' >>> a, b, c = list(str[:2]) + [str[2:]] >>> a,b,c ('l', 'o', 'ng')
(5)的賦值方式則正好是讓變量名少於元素個數的方式。這種賦值形式稱爲序列解包(下文會專門解釋這種賦值方式),多出來的元素會所有以列表的方式賦值給最後一個變量名。正如這裏等價於:
a="l" b=["o", "n", "g"]
下面兩種賦值方式獲得的結果是同樣的,a是字符串,b是列表,b都包含3個元素:
a, *b = ("shell","perl","php","python") a, *b = ["shell","perl","php","python"]
賦值的結果:
shell ['perl', 'php', 'python']
(6)的賦值方式等價於:
b = "long" a = b
python賦值時,老是先計算"="右邊的結果,而後將結果按照賦值方式賦值給"="左邊的變量。因此,這裏的過程是先將"long"賦值給變量b,再將b賦值給變量a。
由於老是先計算右邊,因此交換變量很是的方便。
a, b = "a", "b" # 交換: a, b = b, a # 交換結果: a = "b" b = "a"
(7)的賦值方式a += 3
在結果上等價於a = a + 3
,在其它語言中這兩種賦值方式是徹底等價的,但在python中這種加強賦值的方式要比後者更高效率些,爲何效率要高一些,下文會稍做解釋。在很大程度上來講,Python中只要是簡化的形式,基本上都比更復雜的等價形式效率更高。
(8)的賦值方式((a, b), c) = ('lo', 'ng')
是將序列進行嵌套序列賦值,將'lo'賦值給元組(a, b)
,'ng'賦值給c,元組又進一步賦值a='l', b='o'
。這種賦值方式在python中很好用,特別是在表達式中賦值的時候,好比for循環和函數參數:
for (a, b, c) in [(1, 2, 3), (4, 5, 6)]:... for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]:... def f(((a, b), c)):... f(((1, 2), 3))
在前面簡單介紹了一下序列解包:
a, *b = 'long'
當使用一個*
前綴變量的時候,表示將序列中對應的元素所有收集到一個列表中(注意,老是一個列表),這個列表名爲*
開頭的那個變量名。*
號能夠出如今任意位置處,只要賦值的時候能先後對應位置關係便可。
注意其中的幾個關鍵字:序列、對應的元素、列表
無論如何,收集的結果老是列表,只不過多是空列表或者只有一個元素的列表。
例如:
L = ['aa','bb','cc','dd'] a, *b = L # a='aa',b=['bb','cc','dd'] a, b, *c = L # a='aa',b='bb',c=['cc','dd'] a,*b,d = L # a='aa',d='dd',b=['bb','cc'] *a,d = L # a=['aa','bb','cc'],d='dd' a,b,c,*d = L # a='aa',b='bb',c='cc',d=['dd'] a,b,c,d,*e = L # a='aa',b='bb',c='cc',d='dd',e=[]
兩個注意事項:
a,*b,c,*d = L # 錯誤 *a = L # 錯誤 [*a] = L # 正確 (*a) = L # 正確
之因此單個解包變量時必須放在元組或變量中,看下面兩個等價的例子就很容易理解了:
a, *b = L (a, *b) = L
最後,序列解包是切片的便捷替代方式。能用序列解包的,都能用切片來實現,但切片要輸入額外的各類字符。例如:
a,b,c = L[0],L[1],L[2:] a,b,*c = L
須要注意,解包返回的必定是列表,但序列切片返回的內容則取決於序列的類型。例以下面元組的切片返回的是元組,而不是列表:
>>> T=('aa','bb','cc','dd') >>> a,b,c = T[0],T[1],T[2:] >>> a,b,c ('aa', 'bb', ('cc', 'dd'))
python支持相似於a += 3
這種二元表達式。好比:
a += 3 -> a = a + 3 a -= 3 -> a = a - 3 a *= 3 -> a = a * 3 ...
在python中的某些狀況下,這種二元賦值表達式可能比普通的賦值方式效率更高些。緣由有二:
第一點無需解釋。關於第二點,看下面的例子:
L = [1,2,3] L = L + [4] # (1):慢 L += [4] # (2):快 L.append(4) # (3):快,等價於(2) L = L + [5,6] # (4):慢 L += [5,6] # (5):快 L.extend([5,6]) # (6):快,等價於(5)
對於上面(1)和(4)的一元賦值表達式,先取得L,而後建立一個新的列表對象,將L拷貝到新列表對象中,並將4
或5,6
放進新列表對象,最後賦值給L。這個過程當中涉及到了幾個步驟:新分配內存、內存中列表拷貝、放入新數據。
而(2)(3)是等價的,(5)(6)也是等價的,它們都是直接在內存中的原始列表處修改,不會有拷貝操做,新建的數據對象僅僅只是一個元素。
按照理論上來講,確實二元賦值方式要效率高一些,但要注意的是,列表中保存的只是各元素的引用,因此拷貝列表也僅僅只是拷貝一點引用,這是微乎其微的開銷。因此一元賦值和二元賦值的差距在這一點的性能上基本沒差距,主要的差距還在於一元、二元賦值方式可能存在的表達式不一樣評估次數。
總的來講,使用二元賦值表達式一般能夠做爲可變對象賦值的一種優化手段。