當前位置: 首頁 ? 深入 Python 3 ?

難度級別: ?????

內置數據類型

? Wonder is the foundation of all philosophy, inquiry its progress, ignorance its end. ?
— Michel de Montaigne

 

深入

讓我們暫時將 第一份 Python 程序 拋在腦后,來聊一聊數據類型。在 Python 中, 每個值都有一種數據類型,但您并不需要聲明變量的數據類型。那該方式是如何運作的呢?Python 根據每個變量的初始賦值情況分析其類型,并在內部對其進行跟蹤。

Python 有多種內置數據類型。以下是比較重要的一些:

  1. Booleans[布爾型] 或為 True[真] 或為 False[假]
  2. Numbers[數值型] 可以是 Integers[整數](12)、Floats[浮點數](1.11.2)、Fractions[分數](1/22/3);甚至是 Complex Number[復數]
  3. Strings[字符串型] 是 Unicode 字符序列,例如: 一份 HTML 文檔。
  4. Bytes[字節]Byte Arrays[字節數組]例如: 一份 JPEG 圖像文件。
  5. Lists[列表] 是值的有序序列。
  6. Tuples[元組] 是有序而不可變的值序列。
  7. Sets[集合] 是裝滿無序值的包裹。
  8. Dictionaries[字典] 是鍵值對的無序包裹。

當然,還有更多的類型。在 Python 中一切均為對象,因此存在像 module[模塊]function[函數]class[類]method[方法]file[文件] 甚至 compiled code[已編譯代碼] 這樣的類型。您已經見過這樣一些例子:模塊的 name函數的 docstrings 等等。將學到的包括 《類 迭代器》 中的 Classes[類],以及 《文件》 中的 Files[文件]。

Strings[字符串]和 Bytes[字節串]比較重要,也相對復雜,足以開辟獨立章節予以講述。讓我們先看看其它類型。

?

布爾類型

布爾類型或為真或為假。Python 有兩個被巧妙地命名為 TrueFalse 的常量,可用于對布爾類型的直接賦值。表達式也可以計算為布爾類型的值。在某些地方(如 if 語句),Python 所預期的就是一個可計算出布爾類型值的表達式。這些地方稱為 布爾類型上下文環境。事實上,可在布爾類型上下文環境中使用任何表達式,而 Python 將試圖判斷其真值。在布爾類型上下文環境中,不同的數據類型對于何值為真、何值為假有著不同的規則。(看過本章稍后的實例后,這一點將更好理解。)

例如,看看 humansize.py 中的這個片段:

if size < 0:
    raise ValueError('number must be non-negative')

size 是整數, 0 是整數,而 < 是數字運算符。size < 0 表達式的結果始終是布爾值。可在 Python 交互式 shell 中自行測試下結果:

>>> size = 1
>>> size < 0
False
>>> size = 0
>>> size < 0
False
>>> size = -1
>>> size < 0
True

由于 Python 2 的一些遺留問題,布爾值可以當做數值對待。True1False 為 0 。

>>> True + True
2
>>> True - False
1
>>> True * False
0
>>> True / False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: int division or modulo by zero

喔,喔,喔!別那么干。忘掉我剛才說的。

?

數值類型

數值類型是可畏的。有太多類型可選了。Python 同時支持 Integer[整型]Floating Point[浮點型] 數值。無任何類型聲明可用于區分;Python 通過是否有 小數 點來分辨它們。

>>> type(1)                 
<class 'int'>
>>> isinstance(1, int)      
True
>>> 1 + 1                   
2
>>> 1 + 1.0                 
2.0
>>> type(2.0)
<class 'float'>
  1. 可以使用 type() 函數來檢測任何值或變量的類型。正如所料,1int 類型。
  2. 同樣,還可使用 isinstance() 函數判斷某個值或變量是否為給定某個類型。
  3. 將一個 int 與一個 int 相加將得到一個 int
  4. 將一個 int 與一個 float 相加將得到一個 float 。Python 把 int 強制轉換為 float 以進行加法運算;然后返回一個 float 類型的結果。

將整數強制轉換為浮點數及反向轉換

正如剛才所看到的,一些運算符(如:加法)會根據需把整數強制轉換為浮點數。也可自行對其進行強制轉換。

>>> float(2)                
2.0
>>> int(2.0)                
2
>>> int(2.5)                
2
>>> int(-2.5)               
-2
>>> 1.12345678901234567890  
1.1234567890123457
>>> type(1000000000000000)  
<class 'int'>
  1. 通過調用float() 函數,可以顯示地將 int 強制轉換為 float
  2. 毫不出奇,也可以通過調用 int()float 強制轉換為 int
  3. int() 將進行取整,而不是四舍五入。
  4. 對于負數,int() 函數朝著 0 的方法進行取整。它是個真正的取整(截斷)函數,而不是 floor[地板]函數。
  5. 浮點數精確到小數點后 15 位。
  6. 整數可以任意大。

?Python 2 對于int[整型]long[長整型] 采用不同的數據類型。int 數據類型受到 sys.maxint 的限制,因平臺該限制也會有所不同,但通常是 232-1 。Python 3 只有一種整數類型,其行為方式很有點像 Python 2 的舊 long[長整數] 類型。參閱 PEP 237 了解更多細節。

常見數值運算

對數值可進行各種類型的運算。

>>> 11 / 2      
5.5
>>> 11 // 2     
5
>>> ?11 // 2    
?6
>>> 11.0 // 2   
5.0
>>> 11 ** 2     
121
>>> 11 % 2      
1
  1. / 運算符執行浮點除法。即便分子和分母都是 int,它也返回一個 float 浮點數。
  2. // 運算符執行古怪的整數除法。如果結果為正數,可將其視為朝向小數位取整(不是四舍五入),但是要小心這一點。
  3. 當整數除以負數, // 運算符將結果朝著最近的整數“向上”四舍五入。從數學角度來說,由于 ?6?5 要小,它是“向下”四舍五入,如果期望將結果取整為 ?5,它將會誤導你。
  4. // 運算符并非總是返回整數結果。如果分子或者分母是 float,它仍將朝著最近的整數進行四舍五入,但實際返回的值將會是 float 類型。
  5. ** 運算符的意思是“計算冪”,112 結果為 121
  6. % 運算符給出了進行整除之后的余數。11 除以 2 結果為 5 以及余數 1,因此此處的結果為 1

?在 Python 2 中,運算符 / 通常表示整數除法,但是可以通過在代碼中加入特殊指令,使其看起來像浮點除法。在 Python 3 中,/ 運算符總是表示浮點除法。參閱 PEP 238 了解更多細節。

分數

Python 并不僅僅局限于整數和浮點數類型。它可以完成你在高中階段學過、但幾乎已經全部忘光的所有古怪數學運算。

>>> import fractions              
>>> x = fractions.Fraction(1, 3)  
>>> x
Fraction(1, 3)
>>> x * 2                         
Fraction(2, 3)
>>> fractions.Fraction(6, 4)      
Fraction(3, 2)
>>> fractions.Fraction(0, 0)      
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fractions.py", line 96, in __new__
    raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
  1. 為啟用 fractions 模塊,必先引入 fractions 模塊。
  2. 為定義一個分數,創建一個 Fraction 對象并傳入分子和分母。
  3. 可對分數進行所有的常規數學計算。運算返回一個新的 Fraction 對象。2 * (1/3) = (2/3)
  4. Fraction 對象將會自動進行約分。(6/4) = (3/2)
  5. 在杜絕創建以零為分母的分數方面,Python 有著良好的敏感性。

三角函數

還可在 Python 中進行基本的三角函數運算。

>>> import math
>>> math.pi                
3.1415926535897931
>>> math.sin(math.pi / 2)  
1.0
>>> math.tan(math.pi / 4)  
0.99999999999999989
  1. math 模塊中有一個代表 π 的常量,表示圓的周長與直徑之比率(圓周率)。
  2. math 模塊包括了所有的基本三角函數,包括:sin()cos()tan() 及像 asin() 這樣的變體函數。
  3. 然而要注意的是 Python 并不支持無限精度。tan(π / 4) 將返回 1.0,而不是 0.99999999999999989

布爾上下文環境中的數值

可以在 if 這樣的 布爾類型上下文環境中 使用數值。零值是 false[假],非零值是 true[真]。

>>> def is_it_true(anything):             
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true(1)                         
yes, it's true
>>> is_it_true(-1)
yes, it's true
>>> is_it_true(0)
no, it's false
>>> is_it_true(0.1)                       
yes, it's true
>>> is_it_true(0.0)
no, it's false
>>> import fractions
>>> is_it_true(fractions.Fraction(1, 2))  
yes, it's true
>>> is_it_true(fractions.Fraction(0, 1))
no, it's false
  1. 您知道可以在 Python 交互式 Shell 中定義自己的函數嗎?只需在每行的結尾按 回車鍵 ,然后在某一空行按 回車鍵 結束。
  2. 在布爾類型上下文環境中,非零整數為真;零為假。
  3. 非零浮點數為真; 0.0 為假。請千萬小心這一點!如果有輕微的四舍五入偏差(正如在前面小節中看到的那樣,這并非不可能的事情),那么 Python 將測試 0.0000000000001 而不是 0 ,并將返回一個 True 值。
  4. 分數也可在布爾類型上下文環境中使用。無論 n 為何值,Fraction(0, n) 為假。所有其它分數為真。

?

列表

列表是 Python 的主力數據類型。當提到 “列表 ”時,您腦海中可能會閃現“必須進一步聲明大小的數組,只能包含同一類對象“ 等想法。千萬別這么想。列表比那要酷得多。

? Python 中的列表類似 Perl 5 中的數組。在 Perl 5 中,存儲數組的變量總是以字符 @ 開頭;在 Python 中,變量可隨意命名,Python 僅在內部對數據類型進行跟蹤。

? Python 中的列表更像 Java 中的數組(盡管可以把列表當做生命中所需要的一切來使用)。一個更好的比喻可能是 ArrayList 類,該類可以容納任何對象,并可在添加新元素時進行動態拓展。

創建列表

列表創建非常輕松:使用中括號包裹一系列以逗號分割的值即可。

>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example']  
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[0]                                        
'a'
>>> a_list[4]                                        
'example'
>>> a_list[-1]                                       
'example'
>>> a_list[-3]                                       
'mpilgrim'
  1. 首先,創建一個包含 5 個元素的列表。要注意的是它們保持了最初的順序。這并不是偶然的。列表是元素的有序集合。
  2. 列表可當做以零為基點的數組使用。非空列表的首個元素始終是 a_list[0]
  3. 該 5 元素列表的最后一個元素是 a_list[4],因為列表(索引)總是以零為基點的。
  4. 使用負索引值可從列表的尾部向前計數訪問元素。任何非空列表的最后一個元素總是 a_list[-1]
  5. 如果負數令你混淆,可將其視為如下方式: a_list[-n] == a_list[len(a_list) - n] 。因此在此列表中, a_list[-3] == a_list[5 - 3] == a_list[2]

列表切片

定義列表后,可從其中獲取任何部分作為新列表。該技術稱為對列表進行 切片

>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[1:3]            
['b', 'mpilgrim']
>>> a_list[1:-1]           
['b', 'mpilgrim', 'z']
>>> a_list[0:3]            
['a', 'b', 'mpilgrim']
>>> a_list[:3]             
['a', 'b', 'mpilgrim']
>>> a_list[3:]             
['z', 'example']
>>> a_list[:]              
['a', 'b', 'mpilgrim', 'z', 'example']
  1. 通過指定兩個索引值,可以從列表中獲取稱作“切片”的某個部分。返回值是一個新列表,它包含列表(??切片)中所有元素,按順序從第一個切片索引開始(本例中為 a_list[1]),截止但不包含第二個切片索引(本例中的 a_list[3])。
  2. 如果切片索引之一或兩者均為負數,切片操作仍可進行。如果有幫助的話,您可以這么思考:自左向右讀取列表,第一個切片索引指明了想要的第一個元素,第二個切片索引指明了第一個不想要的元素。返回值是兩者之間的任何值。 between.
  3. 列表是以零為起點的,因此 a_list[0:3] 返回列表的頭三個元素,從 a_list[0] 開始,截止到但不包括 a_list[3]
  4. 如果左切片索引為零,可以將其留空而將零隱去。因此 a_list[:3]a_list[0:3] 是完全相同的,因為起點 0 被隱去了。
  5. 同樣,如果右切片索引為列表的長度,也可以將其留空。因此 a_list[3:]a_list[3:5] 是完全相同的,因為該列表有五個元素。此處有個好玩的對稱現象。在這個五元素列表中, a_list[:3] 返回頭三個元素,而 a_list[3:] 返回最后兩個元素。事實上,無論列表的長度是多少, a_list[:n] 將返回頭 n 個元素,而 a_list[n:] 返回其余部分。
  6. 如果兩個切片索引都留空,那么將包括列表所有的元素。但該返回值與最初的 a_list 變量并不一樣。它是一個新列表,只不過恰好擁有完全相同的元素而已。a_list[:] 是對列表進行復制的一條捷徑。

向列表中新增項

有四種方法可用于向列表中增加元素。

>>> a_list = ['a']
>>> a_list = a_list + [2.0, 3]    
>>> a_list                        
['a', 2.0, 3]
>>> a_list.append(True)           
>>> a_list
['a', 2.0, 3, True]
>>> a_list.extend(['four', 'Ω'])  
>>> a_list
['a', 2.0, 3, True, 'four', 'Ω']
>>> a_list.insert(0, 'Ω')         
>>> a_list
['Ω', 'a', 2.0, 3, True, 'four', 'Ω']
  1. + 運算符連接列表以創建一個新列表。列表可包含任何數量的元素;沒有大小限制(除了可用內存的限制)。然而,如果內存是個問題,那就必須知道在進行連接操作時,將在內存中創建第二個列表。在該情況下,新列表將會立即被賦值給已有變量 a_list 。因此,實際上該行代碼包含兩個步驟 — 連接然后賦值 — 當處理大型列表時,該操作可能(暫時)消耗大量內存。
  2. 列表可包含任何數據類型的元素,單個列表中的元素無須全為同一類型。下面的列表中包含一個字符串、一個浮點數和一個整數。
  3. append() 方法向列表的尾部添加一個新的元素。(現在列表中有 四種 不同數據類型!)
  4. 列表是以類的形式實現的。“創建”列表實際上是將一個類實例化。因此,列表有多種方法可以操作。extend() 方法只接受一個列表作為參數,并將該參數的每個元素都添加到原有的列表中。
  5. insert() 方法將單個元素插入到列表中。第一個參數是列表中將被頂離原位的第一個元素的位置索引。列表中的元素并不一定要是唯一的;比如說:現有兩個各自獨立的元素,其值均為 'Ω':,第一個元素 a_list[0] 以及最后一個元素 a_list[6]

?a_list.insert(0, value) 就像是 Perl 中的 unshift() 函數。它將一個元素添加到列表的頭部,所有其它的元素都被頂理原先的位置以騰出空間。

讓我們進一步看看 append()extend() 的區別。

>>> a_list = ['a', 'b', 'c']
>>> a_list.extend(['d', 'e', 'f'])  
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f']
>>> len(a_list)                     
6
>>> a_list[-1]
'f'
>>> a_list.append(['g', 'h', 'i'])  
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]
>>> len(a_list)                     
7
>>> a_list[-1]
['g', 'h', 'i']
  1. extend() 方法只接受一個參數,而該參數總是一個列表,并將列表 a_list 中所有的元素都添加到該列表中。
  2. 如果開始有個 3 元素列表,然后將它與另一個 3 元素列表進行 extend 操作,結果是將獲得一個 6 元素列表。
  3. 另一方面, append() 方法只接受一個參數,但可以是任何數據類型。在此,對一個 3 元素列表調用 append() 方法。
  4. 如果開始的時候有個 6 元素列表,然后將一個列表 append[添加]上去,結果就會……得到一個 7 元素列表。為什么是 7 個?因為最后一個元素(剛剛 append[添加] 的元素) 本身是個列表 。列表可包含任何類型的數據,包括其它列表。這可能是你所需要的結果,也許不是。但如果這就是你想要的,那這就是你所得到的。

在列表中檢索值

>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list.count('new')       
2
>>> 'new' in a_list           
True
>>> 'c' in a_list
False
>>> a_list.index('mpilgrim')  
3
>>> a_list.index('new')       
2
>>> a_list.index('c')         
Traceback (innermost last):
  File "<interactive input>", line 1, in ?ValueError: list.index(x): x not in list
  1. 如你所期望, count() 方法返回了列表中某個特定值出現的次數。
  2. 如果你想知道的是某個值是否出現在列表中, in 運算符將會比使用 count() 方法要略快一些。in 運算符總是返回 TrueFalse;它不會告訴你該值出現在什么位置。
  3. 如果想知道某個值在列表中的精確位置,可調用 index() 方法。盡管可以通過第二個參數(以 0 為基點的)索引值來指定起點,通過第三個參數(以 0 基點的)索引來指定搜索終點,但缺省情況下它將搜索整個列表,
  4. index() 方法將查找某值在列表中的第一次出現。在該情況下,'new' 在列表中出現了兩次,分別為 a_list[2]a_list[4],但 index() 方法將只返回第一次出現的位置索引值。
  5. 可能 出乎 您的預期,如果在列表中沒有找到該值,index() 方法將會引發一個例外。

等等,什么?是這樣的:如果沒有在列表中找到該值, index() 方法將會引發一個例外。這是 Python 語言最顯著不同之處,其它多數語言將會返回一些無效的索引值(像是 -1)。當然,一開始這一點看起來比較討厭,但我想您會逐漸欣賞它。這意味著您的程序將會在問題的源頭處崩潰,而不是之后奇怪地、默默地崩潰。請記住, -1 是合法的列表索引值。如果 index() 方法返回 -1,可能會導致調整過程變得不那么有趣!

從列表中刪除元素

列表可以自動拓展或者收縮。您已經看到了拓展部分。也有幾種方法可從列表中刪除元素。

>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list[1]
'b'
>>> del a_list[1]         
>>> a_list
['a', 'new', 'mpilgrim', 'new']
>>> a_list[1]             
'new'
  1. 可使用 del 語句從列表中刪除某個特定元素。
  2. 刪除索引 1 之后再訪問索引 1不會 導致錯誤。被刪除元素之后的所有元素將移動它們的位置以“填補”被刪除元素所產生的“縫隙”。

不知道位置索引?這不成問題,您可以通過值而不是索引刪除元素。

>>> a_list.remove('new')  
>>> a_list
['a', 'mpilgrim', 'new']
>>> a_list.remove('new')  
>>> a_list
['a', 'mpilgrim']
>>> a_list.remove('new')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
  1. 還可以通過 remove() 方法從列表中刪除某個元素。remove() 方法接受一個 value 參數,并刪除列表中該值的第一次出現。同樣,被刪除元素之后的所有元素將會將索引位置下移,以“填補縫隙”。列表永遠不會有“縫隙”。
  2. 您可以盡情地調用 remove() 方法,但如果試圖刪除列表中不存在的元素,它將引發一個例外。

Removing Items From A List: Bonus Round

另一有趣的列表方法是 pop()pop() 方法是從列表刪除元素的另一方法,但有點變化。

>>> a_list = ['a', 'b', 'new', 'mpilgrim']
>>> a_list.pop()   
'mpilgrim'
>>> a_list
['a', 'b', 'new']
>>> a_list.pop(1)  
'b'
>>> a_list
['a', 'new']
>>> a_list.pop()
'new'
>>> a_list.pop()
'a'
>>> a_list.pop()   
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop from empty list
  1. 如果不帶參數調用, pop() 列表方法將刪除列表中最后的元素,并返回所刪除的值
  2. 可以從列表中 pop[彈出]任何元素。只需傳給 pop() 方法一個位置索引值。它將刪除該元素,將其后所有元素移位以“填補縫隙”,然后返回它刪除的值。
  3. 對空列表調用 pop() 將會引發一個例外。

?不帶參數調用的 pop() 列表方法就像 Perl 中的 pop() 函數。它從列表中刪除最后一個元素并返回所刪除元素的值。Perl 還有另一個函數 shift(),可用于刪除第一個元素并返回其值;在 Python 中,該函數相當于 a_list.pop(0)

布爾上下文環境中的列表

可以在 if 這樣的 布爾類型上下文環境中 使用列表。

>>> def is_it_true(anything):
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true([])             
no, it's false
>>> is_it_true(['a'])          
yes, it's true
>>> is_it_true([False])        
yes, it's true
  1. 在布爾類型上下文環境中,空列表為假值。
  2. 任何至少包含一個上元素的列表為真值。
  3. 任何至少包含一個上元素的列表為真值。元素的值無關緊要。

?

元組

元素 是不可變的列表。一旦創建之后,用任何方法都不可以修改元素。

>>> a_tuple = ("a", "b", "mpilgrim", "z", "example")  
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple[0]                                        
'a'
>>> a_tuple[-1]                                       
'example'
>>> a_tuple[1:3]                                      
('b', 'mpilgrim')
  1. 元組的定義方式和列表相同,除了整個元素的集合都用圓括號,而不是方括號閉合。
  2. 和列表一樣,元組的元素都有確定的順序。元組的索引也是以零為基點的,和列表一樣,因此非空元組的第一個元素總是 a_tuple[0]
  3. 負的索引從元組的尾部開始計數,這和列表也是一樣的。
  4. 和列表一樣,元組也可以進行切片操作。對列表切片可以得到新的列表;對元組切片可以得到新的元組。

元組和列表的主要區別是元組不能進行修改。用技術術語來說,元組是 不可變更 的。從實踐的角度來說,沒有可用于修改元組的方法。列表有像 append()extend()insert()remove()pop() 這樣的方法。這些方法,元組都沒有。可以對元組進行切片操作(因為該方法創建一個新的元組),可以檢查元組是否包含了特定的值(因為該操作不修改元組),還可以……就那么多了。

# continued from the previous example
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple.append("new")               
Traceback (innermost last):
  File "<interactive input>", line 1, in ?AttributeError: 'tuple' object has no attribute 'append'
>>> a_tuple.remove("z")                 
Traceback (innermost last):
  File "<interactive input>", line 1, in ?AttributeError: 'tuple' object has no attribute 'remove'
>>> a_tuple.index("example")            
4
>>> "z" in a_tuple                      
True
  1. 無法向元組添加元素。元組沒有 append()extend() 方法。
  2. 不能從元組中刪除元素。元組沒有 remove()pop() 方法。
  3. 可以 在元組中查找元素,由于該操作不改變元組。
  4. 還可以使用 in 運算符檢查某元素是否存在于元組中。

那么元組有什么好處呢?

?元組可轉換成列表,反之亦然。內建的 tuple() 函數接受一個列表參數,并返回一個包含同樣元素的元組,而 list() 函數接受一個元組參數并返回一個列表。從效果上看, tuple() 凍結列表,而 list() 融化元組。

布爾上下文環境中的元組

可以在 if 這樣的 布爾類型上下文環境中 使用元組。

>>> def is_it_true(anything):
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true(())             
no, it's false
>>> is_it_true(('a', 'b'))     
yes, it's true
>>> is_it_true((False,))       
yes, it's true
>>> type((False))              
<class 'bool'>
>>> type((False,))
<class 'tuple'>
  1. 在布爾類型上下文環境中,空元組為假值。
  2. 任何至少包含一個上元素的元組為真值。
  3. 任何至少包含一個上元素的元組為真值。元素的值無關緊要。不過此處的逗號起什么作用呢?
  4. 為創建單元素元組,需要在值之后加上一個逗號。沒有逗號,Python 會假定這只是一對額外的圓括號,雖然沒有害處,但并不創建元組。

同時賦多個值

以下是一種很酷的編程捷徑:在 Python 中,可使用元組來一次賦多值。

>>> v = ('a', 2, True)
>>> (x, y, z) = v       
>>> x
'a'
>>> y
2
>>> z
True
  1. v 是一個三元素的元組,而 (x, y, z) 是包含三個變量的元組。將其中一個賦值給另一個將會把 v 中的每個值按順序賦值給每一個變量。

該特性有多種用途。假設需要將某個名稱指定某個特定范圍的值。可以使用內建的 range() 函數進行多變量賦值以快速地進行連續變量賦值。

>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)  
>>> MONDAY                                                                       
0
>>> TUESDAY
1
>>> SUNDAY
6
  1. 內建的 range() 函數構造了一個整數序列。(從技術上來說, range() 函數返回的既不是列表也不是元組,而是一個 迭代器,但稍后您將學到它們的區別。) MONDAYTUESDAYWEDNESDAYTHURSDAYFRIDAYSATURDAYSUNDAY 是您所定義的變量。(本例來自于 calendar 模塊,該短小而有趣的模塊打印日歷,有點像 UNIX 程序 cal 。該 calendar 模塊為星期數定義了整數常量。
  2. 現在,每個變量都有其值了: MONDAY 為 0, TUESDAY1,如此類推。

還可以使用多變量賦值創建返回多值的函數,只需返回一個包含所有值的元組。調用者可將返回值視為一個簡單的元組,或將其賦值給不同的變量。許多標準 Python 類庫這么干,包括在下一章將學到的 os 模塊。

?

集合

集合set 是裝有獨特值的無序“袋子”。一個簡單的集合可以包含任何數據類型的值。如果有兩個集合,則可以執行像聯合、交集以及集合求差等標準集合運算。

創建集合

重中之重。創建集合非常簡單。

>>> a_set = {1}     
>>> a_set
{1}
>>> type(a_set)     
<class 'set'>
>>> a_set = {1, 2}  
>>> a_set
{1, 2}
  1. 要創建只包含一個值的集合,僅需將該值放置于花括號之間。({})。
  2. 實際上,集合以 的形式實現,但目前還無須考慮這一點。
  3. 要創建多值集合,請將值用逗號分開,并用花括號將所有值包裹起來。

還可以 列表 為基礎創建集合。

>>> a_list = ['a', 'b', 'mpilgrim', True, False, 42]
>>> a_set = set(a_list)                           
>>> a_set                                         
{'a', False, 'b', True, 'mpilgrim', 42}
>>> a_list                                        
['a', 'b', 'mpilgrim', True, False, 42]
  1. 要從列表創建集合,可使用 set() 函數。(懂得如何實現集合的學究可能指出這實際上并不是調用某個函數,而是對某個類進行實例化。我保證在本書稍后的地方將會學到其中的區別。目前而言,僅需知道 set() 行為與函數類似,以及它返回一個集合。)
  2. 正如我之前提到的,簡單的集合可以包括任何數據類型的值。而且,如我之前所提到的,集合是 無序的。該集合并不記得用于創建它的列表中元素的最初順序。如果向集合中添加元素,它也不會記得添加的順序。
  3. 初始的列表并不會發生變化。

還沒有任何值?沒有問題。可以創建一個空的集合。

>>> a_set = set()    
>>> a_set            
set()
>>> type(a_set)      
<class 'set'>
>>> len(a_set)       
0
>>> not_sure = {}    
>>> type(not_sure)
<class 'dict'>
  1. 要創建空集合,可不帶參數調用 set()
  2. 打印出來的空集合表現形式看起來有點兒怪。也許,您期望看到一個 {} 吧 ?該符號表示一個空的字典,而不是一個空的集合。本章稍后您將學到關于字典的內容。
  3. 盡管打印出的形式奇怪,這 確實是 一個集合……
  4. …… 同時該集合沒有任何成員。
  5. 由于從 Python 2 沿襲而來歷史的古怪規定,不能使用兩個花括號來創建空集合。該操作實際創建一個空字典,而不是一個空集合。

修改集合

有兩種方法可向現有集合中添加值: add() 方法和 update() 方法。

>>> a_set = {1, 2}
>>> a_set.add(4)  
>>> a_set
{1, 2, 4}
>>> len(a_set)    
3
>>> a_set.add(1)  
>>> a_set
{1, 2, 4}
>>> len(a_set)    
3
  1. add() 方法接受單個可以是任何數據類型的參數,并將該值添加到集合之中。
  2. 該集合現在有三個成員了。
  3. 集合是裝 唯一值 的袋子。如果試圖添加一個集合中已有的值,將不會發生任何事情。將不會引發一個錯誤;只是一條空操作。
  4. 該集合 仍然 只有三個成員。
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set.update({2, 4, 6})                       
>>> a_set                                         
{1, 2, 3, 4, 6}
>>> a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13})  
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13}
>>> a_set.update([10, 20, 30])                    
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}
  1. update() 方法僅接受一個集合作為參數,并將其所有成員添加到初始列表中。其行為方式就像是對參數集合中的每個成員調用 add() 方法。
  2. 由于集合不能包含重復的值,因此重復的值將會被忽略。
  3. 實際上,可以帶任何數量的參數調用 update() 方法。如果調用時傳遞了兩個集合, update() 將會被每個集合中的每個成員添加到初始的集合當中(丟棄重復值)。
  4. update() 方法還可接受一些其它數據類型的對象作為參數,包括列表。如果調用時傳入列表,update() 將會把列表中所有的元素添加到初始集合中。

從集合中刪除元素

有三種方法可以用來從集合中刪除某個值。前兩種,discard()remove() 有細微的差異。

>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10)                        
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10)                        
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.remove(21)                         
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21)                         
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 21
  1. discard() 接受一個單值作為參數,并從集合中刪除該值。
  2. 如果針對一個集合中不存在的值調用 discard() 方法,它不進行任何操作。不產生錯誤;只是一條空指令。
  3. remove() 方法也接受一個單值作為參數,也從集合中將其刪除。
  4. 區別在這里:如果該值不在集合中,remove() 方法引發一個 KeyError 例外。

就像列表,集合也有個 pop() 方法。

>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop()                                
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.clear()                              
>>> a_set
set()
>>> a_set.pop()                                
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'pop from an empty set'
  1. pop() 方法從集合中刪除某個值,并返回該值。然而,由于集合是無序的,并沒有“最后一個”值的概念,因此無法控制刪除的是哪一個值。它基本上是隨機的。
  2. clear() 方法刪除集合中 所有 的值,留下一個空集合。它等價于 a_set = set(),該語句創建一個新的空集合,并用之覆蓋 a_set 變量的之前的值。
  3. 試圖從空集合中彈出某值將會引發 KeyError 例外。

常見集合操作

Python 的 集合 類型支持幾種常見的運算。

>>> a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
>>> 30 in a_set                                                     
True
>>> 31 in a_set
False
>>> b_set = {1, 2, 3, 5, 6, 8, 9, 12, 15, 17, 18, 21}
>>> a_set.union(b_set)                                              
{1, 2, 195, 4, 5, 6, 8, 12, 76, 15, 17, 18, 3, 21, 30, 51, 9, 127}
>>> a_set.intersection(b_set)                                       
{9, 2, 12, 5, 21}
>>> a_set.difference(b_set)                                         
{195, 4, 76, 51, 30, 127}
>>> a_set.symmetric_difference(b_set)                               
{1, 3, 4, 6, 8, 76, 15, 17, 18, 195, 127, 30, 51}
  1. 要檢測某值是否是集合的成員,可使用 in 運算符。其工作原理和列表的一樣。
  2. union() 方法返回一個新集合,其中裝著 在兩個 集合中出現的元素。
  3. intersection() 方法返回一個新集合,其中裝著 同時 在兩個集合中出現的所有元素。
  4. difference() 方法返回的新集合中,裝著所有在 a_set 出現但未在 b_set 中的元素。
  5. symmetric_difference() 方法返回一個新集合,其中裝著所有 只在其中一個 集合中出現的元素。

這三種方法是對稱的。

# continued from the previous example
>>> b_set.symmetric_difference(a_set)                                       
{3, 1, 195, 4, 6, 8, 76, 15, 17, 18, 51, 30, 127}
>>> b_set.symmetric_difference(a_set) == a_set.symmetric_difference(b_set)  
True
>>> b_set.union(a_set) == a_set.union(b_set)                                
True
>>> b_set.intersection(a_set) == a_set.intersection(b_set)                  
True
>>> b_set.difference(a_set) == a_set.difference(b_set)                      
False
  1. a_setb_set 的對稱差分 看起來b_seta_set 的對稱差分不同,但請記住:集合是無序的。任何兩個包含所有同樣值(無一遺漏)的集合可認為是相等的。
  2. 而這正是這里發生的事情。不要被 Python Shell 對這些集合的輸出形式所愚弄了。它們包含相同的值,因此是相等的。
  3. 對兩個集合的 Union[并集]操作也是對稱的。
  4. 對兩個集合的 Intersection[交集]操作也是對稱的。
  5. 對兩個集合的 Difference[求差]操作不是對稱的。這是有意義的;它類似于從一個數中減去另一個數。操作數的順序會導致結果不同。

最后,有幾個您可能會問到的問題。

>>> a_set = {1, 2, 3}
>>> b_set = {1, 2, 3, 4}
>>> a_set.issubset(b_set)    
True
>>> b_set.issuperset(a_set)  
True
>>> a_set.add(5)             
>>> a_set.issubset(b_set)
False
>>> b_set.issuperset(a_set)
False
  1. a_setb_set 的 子集 — 所有 a_set 的成員均為 b_set 的成員。
  2. 同樣的問題反過來說, b_seta_set超集,因為 a_set 的所有成員均為 b_set 的成員。
  3. 一旦向 a_set 添加一個未在 b_set 中出現的值,兩項測試均返回 False

布爾上下文環境中的集合

可在 if 這樣的 布爾類型上下文環境中 使用集合。

>>> def is_it_true(anything):
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true(set())          
no, it's false
>>> is_it_true({'a'})          
yes, it's true
>>> is_it_true({False})        
yes, it's true
  1. 在布爾類型上下文環境中,空集合為假值。
  2. 任何至少包含一個上元素的集合為真值。
  3. 任何至少包含一個上元素的集合為真值。元素的值無關緊要。

?

字典

字典 是鍵值對的無序集合。向字典添加一個鍵的同時,必須為該鍵增添一個值。(之后可隨時修改該值。) Python 的字典為通過鍵獲取值進行了優化,而不是反過來。

?Python 中的字典與 Perl 5 中的 hash [散列]類似。在 Perl 5 中,散列存儲的變量總是以一個 % 符開頭。在 Python 中,變量可以隨意命名,而 Python 內部跟蹤其數據類型。

創建字典

創建字典非常簡單。其語法與 集合 的類似,但應當指定鍵值對而不是值。有了字典后,可以通過鍵來查找值。

>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'}  
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['server']                                                    
'db.diveintopython3.org'
>>> a_dict['database']                                                  
'mysql'
>>> a_dict['db.diveintopython3.org']                                    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'db.diveintopython3.org'
  1. 首先,通過將兩個字典項指定給 a_dict 變量創建了一個新字典。每個字典項都是一組鍵值對,整個字典項集合都被大括號包裹在內。
  2. 'server' 為鍵,通過 a_dict['server'] 引用的關聯值為 'db.diveintopython3.org'
  3. 'database' 為鍵,通過 a_dict['database'] 引用的關聯值為 'mysql'
  4. 可以通過鍵獲取值,但不能通過值獲取鍵。因此 a_dict['server']'db.diveintopython3.org',而 a_dict['db.diveintopython3.org'] 會引發例外,因為 'db.diveintopython3.org' 并不是鍵。

修改字典

字典沒有預定義的大小限制。可以隨時向字典中添加新的鍵值對,或者修改現有鍵所關聯的值。繼續前面的例子:

>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['database'] = 'blog'  
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'blog'}
>>> a_dict['user'] = 'mark'      
>>> a_dict                       
{'server': 'db.diveintopython3.org', 'user': 'mark', 'database': 'blog'}
>>> a_dict['user'] = 'dora'      
>>> a_dict
{'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
>>> a_dict['User'] = 'mark'      
>>> a_dict
{'User': 'mark', 'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
  1. 在字典中不允許有重復的鍵。對現有的鍵賦值將會覆蓋舊值。
  2. 可隨時添加新的鍵值對。該語法與修改現有值相同。
  3. 新字典項(鍵為 'user',值為 'mark')出現在中間。事實上,在第一個例子中字典項按順序出現是個巧合;現在它們不按順序出現同樣也是個巧合。
  4. 對既有字典鍵進行賦值只會用新值替代舊值。
  5. 該操作會將 user 鍵的值改回 "mark" 嗎?不會!仔細看看該鍵——有個大寫的 U 出現在 "User" 中。字典鍵是區分大小寫的,因此該語句創建了一組新的鍵值對,而不是覆蓋既有的字典項。對你來說它們可能是一樣的,但對于 Python 而言它們是完全不同的。

混合值字典

字典并非只能用于字符串。字典的值可以是任何數據類型,包括整數、布爾值、任何對象,甚至是其它的字典。而且就算在同一字典中,所有的值也無須是同一類型,您可根據需要混合匹配。字典的鍵要嚴格得多,可以是字符串、整數和其它一些類型。在同一字典中也可混合、匹配使用不同數據類型的鍵。

實際上,您已經在 your first Python program 見過一個將非字符串用作鍵的字典了。

SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

讓我們在交互式 shell 中剖析一下:

>>> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
...             1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
>>> len(SUFFIXES)      
2
>>> 1000 in SUFFIXES   
True
>>> SUFFIXES[1000]     
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
>>> SUFFIXES[1024]     
['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
>>> SUFFIXES[1000][3]  
'TB'
  1. 類似 列表集合len() 函數將返回字典中鍵的數量。
  2. 而且像列表和集合一樣,可使用 in 運算符以測試某個特定的鍵是否在字典中。
  3. 1000 字典 SUFFIXES 的一個鍵;其值為一個 8 元素列表(確切地說,是 8 個字符串)。
  4. 同樣, 1024 是字典 SUFFIXES 的鍵;其值也是一個 8 元素列表。
  5. 由于 SUFFIXES[1000] 是列表,可以通過它們的 0 基點索引來獲取列表中的單個元素。

布爾上下文環境中的字典

可以在 if 這樣的 布爾類型上下文環境中 使用字典。

>>> def is_it_true(anything):
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true({})             
no, it's false
>>> is_it_true({'a': 1})       
yes, it's true
  1. 在布爾類型上下文環境中,空字典為假值。
  2. 至少包含一個鍵值對的字典為真值。

?

None

None 是 Python 的一個特殊常量。它是一個 值。NoneFalse 不同。None 不是 0 。None 不是空字符串。將 None 與任何非 None 的東西進行比較將總是返回 False

None 是唯一的空值。它有著自己的數據類型(NoneType)。可將 None 賦值給任何變量,但不能創建其它 NoneType 對象。所有值為 None 變量是相等的。

>>> type(None)
<class 'NoneType'>
>>> None == False
False
>>> None == 0
False
>>> None == ''
False
>>> None == None
True
>>> x = None
>>> x == None
True
>>> y = None
>>> x == y
True

布爾上下文環境中的 None

布爾類型上下文環境中None 為假值,而 not None 為真值。

>>> def is_it_true(anything):
...   if anything:
...     print("yes, it's true")
...   else:
...     print("no, it's false")
...
>>> is_it_true(None)
no, it's false
>>> is_it_true(not None)
yes, it's true

?

深入閱讀

? 2001–9 Mark Pilgrim

<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            亚洲欧美在线