dictionary
我們都曾經使用過語言詞典來查找不認識的單詞的定義。語言詞典針對給定的單詞(比如 python)提供一組標準的信息。這種系統將定義和其他信息與實際的單詞關聯(映射)起來。使用單詞作為鍵定位器來尋找感興趣的信息。這種概念延伸到 Python 編程語言中,就成了特殊的容器類型,稱為 dictionary。
dictionary 數據類型在許多語言中都存在。它有時候稱為關聯 數組(因為數據與一個鍵值相關聯),或者作為散列表。但是在 Python 中,dictionary 是一個很好的對象,因此即使是編程新手也很容易在自己的程序中使用它。按照正式的說法,Python 中的 dictionary 是一種異構的、易變的映射容器數據類型。
創建 dictionary
本系列中前面的文章介紹了 Python 編程語言中的一些容器數據類型,包括 tuple、string 和 list(參見 參考資料)。這些容器的相似之處是它們都是基于序列的。這意味著要根據元素在序列中的位置訪問這些集合中的元素。所以,給定一個名為 a 的序列,就可以使用數字索引(比如 a[0] )或片段(比如 a[1:5])來訪問元素。Python 中的 dictionary 容器類型與這三種容器類型的不同之處在于,它是一個無序的集合。不是按照索引號,而是使用鍵值來訪問集合中的元素。這意味著構造 dictionary 容器比 tuple、string 或 list 要復雜一些,因為必須同時提供鍵和相應的值,如清單 1 所示。
清單 1. 在 Python 中創建 dictionary,第 1 部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
>>> d = { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } >>> d { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } >>> len (d) >>> type (d) # Base object is the dict class < type 'dict' > >>> d = {} # Create an empty dictionary >>> len (d) >>> d = { 1 : 'one' } # Create a single item dictionary >>> d { 1 : 'one' } >>> len (d) >>> d = { 'one' : 1 } # The key value can be non-numeric >>> d { 'one' : 1 } >>> d = { 'one' : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]} >>> d { 'one' : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]} |
如這個例子所示,在 Python 中創建 dictionary 要使用花括號和以冒號分隔的鍵-值組合。如果沒有提供鍵-值組合,那么就會創建一個空的 dictionary。使用一個鍵-值組合,就會創建具有一個元素的 dictionary,以此類推,直至您需要的任何規模。與任何容器類型一樣,可以使用內置的 len 方法查明集合中元素的數量。
前面的示例還演示了關于 dictionary 容器的另一個重要問題。鍵并不限制為整數;它可以是任何不易變的數據類型,包括 integer、float、tuple 或 string。因為 list 是易變的,所以它不能作為 dictionary 中的鍵。但是 dictionary 中的值可以是任何數據類型的。
最后,這個示例說明了 Python 中 dictionary 的底層數據類型是 dict 對象。要進一步了解如何使用 Python 中的 dictionary,可以使用內置的幫助解釋器來了解 dict 類,如清單 2 所示。
清單 2. 獲得關于 dictionary 的幫助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
>>> help ( dict )on class dict in module __builtin__: dict ( object ) | dict () - > new empty dictionary. | dict (mapping) - > new dictionary initialized from a mapping object 's | (key, value) pairs. | dict (seq) - > new dictionary initialized as if via: | d = {} | for k, v in seq: | d[k] = v | dict ( * * kwargs) - > new dictionary initialized with the name = value pairs | in the keyword argument list . For example: dict (one = 1 , two = 2 ) | | Methods defined here: | | __cmp__(...) | x.__cmp__(y) < = = > cmp (x,y) | | __contains__(...) | x.__contains__(y) < = = > y in x | | __delitem__(...) | x.__delitem__(y) < = = > del x[y] ... |
關于 dict 類的幫助指出,可以使用構造函數直接創建 dictionary,而不使用花括號。既然與其他容器數據類型相比,在創建 dictionary 時必須提供更多的數據,那么這些創建方法比較復雜也就不足為奇了。但是,在實踐中使用 dictionary 并不難,如清單 3 所示。
清單 3. 在 Python 中創建 dictionary,第 2 部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
>>> l = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] >>> d = dict (l)(most recent call last): File "<stdin>" , line 1 , in ?: can't convert dictionary update sequence element #0 to a sequence >>> l = [( 0 , 'zero' ), ( 1 , 'one' ), ( 2 , 'two' ), ( 3 , 'three' )] >>> d = dict (l) >>> d { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' } >>> l = [[ 0 , 'zero' ], [ 1 , 'one' ], [ 2 , 'two' ], [ 3 , 'three' ]] >>> d { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' } >>> d = dict (l) >>> d { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' } >>> d = dict (zero = 0 , one = 1 , two = 2 , three = 3 ) >>> d { 'zero' : 0 , 'three' : 3 , 'two' : 2 , 'one' : 1 } >>> d = dict ( 0 = zero, 1 = one, 2 = two, 3 = three): keyword can't be an expression |
可以看到,創建 dictionary 需要鍵值和數據值。第一次從 list 創建 dictionary 的嘗試失敗了,這是因為沒有匹配的鍵-數據值對。第二個和第三個示例演示了如何正確地創建 dictionary:在第一種情況下,使用一個 list,其中的每個元素都是一個 tuple;在第二種情況下,也使用一個 list,但是其中的每個元素是另一個 list。在這兩種情況下,內層容器都用于獲得鍵到數據值的映射。
直接創建 dict 容器的另一個方法是直接提供鍵到數據值的映射。這種技術允許顯式地定義鍵和與其對應的值。這個方法其實用處不大,因為可以使用花括號完成相同的任務。另外,如前面的例子所示,在采用這種方式時對于鍵不能使用數字,否則會導致拋出一個異常。
訪問和修改 dictionary
創建了 dictionary 之后,需要訪問其中包含的數據。訪問方式與訪問任何 Python 容器數據類型中的數據相似,如清單 4 所示。
清單 4. 訪問 dictionary 中的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> d = dict (zero = 0 , one = 1 , two = 2 , three = 3 ) >>> d { 'zero' : 0 , 'three' : 3 , 'two' : 2 , 'one' : 1 } >>> d[ 'zero' ] >>> d[ 'three' ] >>> d = { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } >>> d[ 0 ] 'zero' >>> d[ 4 ] 'four' >>> d[ 6 ](most recent call last): File "<stdin>" , line 1 , in ?: 6 >>> d[: - 1 ](most recent call last): File "<stdin>" , line 1 , in ?: unhashable type |
可以看到,從 dictionary 中獲取數據值的過程幾乎與從任何容器類型中獲取數據完全一樣。在容器名后面的方括號中放上鍵值。當然,dictionary 可以具有非數字的鍵值,如果您以前沒有使用過這種數據類型,那么適應這一點需要些時間。因為在 dictionary 中次序是不重要的(dictionary 中數據的次序是任意的),所以可以對其他容器數據類型使用的片段功能,對于 dictionary 是不可用的。試圖使用片段或者試圖從不存在的鍵訪問數據就會拋出異常,指出相關的錯誤。
Python 中的 dictionary 容器也是易變的數據類型,這意味著在創建它之后可以修改它。如清單 5 所示,可以添加新的鍵到數據值的映射,可以修改現有的映射,還可以刪除映射。
清單 5. 修改 dictionary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> d = { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' } >>> d[ 0 ] 'zero' >>> d[ 0 ] = 'Zero' >>> d { 0 : 'Zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' } >>> d[ 4 ] = 'four' >>> d[ 5 ] = 'five' >>> d { 0 : 'Zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } >>> del d[ 0 ] >>> d { 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } >>> d[ 0 ] = 'zero' >>> d { 0 : 'zero' , 1 : 'one' , 2 : 'two' , 3 : 'three' , 4 : 'four' , 5 : 'five' } |
清單 5 演示了幾個重點。首先,修改數據值是很簡單的:將新的值分配給適當的鍵。其次,添加新的鍵到數據值的映射也很簡單:將相關數據分配給新的鍵值。Python 自動進行所有處理。不需要調用 append 這樣的特殊方法。對于 dictionary 容器,次序是不重要的,所以這應該好理解,因為不是在 dictionary 后面附加映射,而是將它添加到容器中。最后,刪除映射的辦法是使用 del 操作符以及應該從容器中刪除的鍵。
在清單 5 中有一個情況看起來有點兒怪,鍵值是按照數字次序顯示的,而且這個次序與插入映射的次序相同。不要誤解 —— 情況不總是這樣的。Python dictionary 中映射的次序是任意的,對于不同的 Python 安裝可能會有變化,甚至多次使用同一 Python 解釋器運行相同代碼也會有變化。如果在一個 dictionary 中使用不同類型的鍵和數據值,那么就很容易看出這一點,如清單 6 所示。
清單 6. 異構的容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
>>> d = { 0 : 'zero' , 'one' : 1 } >>> d { 0 : 'zero' , 'one' : 1 } >>> d[ 0 ] 'zero' >>> type (d[ 0 ]) < type 'str' > >>> d[ 'one' ] >>> type (d[ 'one' ]) < type 'int' > >>> d[ 'two' ] = [ 0 , 1 , 2 ] >>> d { 0 : 'zero' , 'two' : [ 0 , 1 , 2 ], 'one' : 1 } >>> d[ 3 ] = ( 0 , 1 , 2 , 3 ) >>> d { 0 : 'zero' , 3 : ( 0 , 1 , 2 , 3 ), 'two' : [ 0 , 1 , 2 ], 'one' : 1 } >>> d[ 3 ] = 'a tuple' >>> d { 0 : 'zero' , 3 : 'a tuple' , 'two' : [ 0 , 1 , 2 ], 'one' : 1 } |
如這個例子所示,可以在一個 dictionary 中使用不同數據類型的鍵和數據值。還可以通過修改 dictionary 添加新的類型。最后,產生的 dictionary 的次序并不與插入數據的次序匹配。本質上,dictionary 中元素的次序是由 Python dictionary 數據類型的實際實現控制的。新的 Python 解釋器很容易改變這一次序,所以一定不要依賴于元素在 dictionary 中的特定次序。
用 dictionary 進行編程
作為正式的 Python 數據類型,dictionary 支持其他較簡單數據類型所支持的大多數操作。這些操作包括一般的關系操作符,比如 <、> 和 ==,如清單 7 所示。
清單 7. 一般關系操作符
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> d1 = { 0 : 'zero' } >>> d2 = { 'zero' : 0 } >>> d1 < d2 >>> d2 = d1 >>> d1 < d2 >>> d1 = = d2 >>> id (d1) >>> id (d2) >>> d2 = d1.copy() >>> d1 = = d2 >>> id (d1) >>> id (d2) |
前面的示例創建兩個 dictionary 并使用它們測試 < 關系操作符。盡管很少以這種方式比較兩個 dictionary;但是如果需要,可以這樣做。
然后,這個示例將賦值給變量 d1 的 dictionary 賦值給另一個變量 d2。注意,內置的 id() 方法對于 d1 和 d2 返回相同的標識符值,這說明這不是復制操作。要想復制 dictionary ,可以使用 copy() 方法。從這個示例中的最后幾行可以看出,副本與原來的 dictionary 完全相同,但是容納這個 dictionary 的變量具有不同的標識符。
在 Python 程序中使用 dictionary 時,很可能希望檢查 dictionary 中是否包含特定的鍵或值。如清單 8 所示,這些檢查很容易執行。
清單 8. 條件測試和 dictionary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> d = { 0 : 'zero' , 3 : 'a tuple' , 'two' : [ 0 , 1 , 2 ], 'one' : 1 } >>> d.keys() [ 0 , 3 , 'two' , 'one' ] >>> if 0 in d.keys(): ... print 'True' ... >>> if 'one' in d: ... print 'True' ... >>> if 'four' in d: ... print 'Dictionary contains four' ... elif 'two' in d: ... print 'Dictionary contains two' ... contains two |
測試 dictionary 中鍵或數據值的成員關系是很簡單的。dictionary 容器數據類型提供幾個內置方法,包括 keys() 方法和 values() 方法(這里沒有演示)。這些方法返回一個列表,其中分別包含進行調用的 dictionary 中的鍵或數據值。
因此,要判斷某個值是否是 dictionary 中的鍵,應該使用 in 操作符檢查這個值是否在調用 keys() 方法所返回的鍵值列表中??梢允褂孟嗨频牟僮鳈z查某個值是否在調用 values() 方法所返回的數據值列表中。但是,可以使用 dictionary 名作為簡寫表示法。這是有意義的,因為一般希望知道某個數據值(而不是鍵值)是否在 dictionary 中。
在 “Discover Python, Part 6” 中,您看到了使用 for 循環遍歷容器中的元素是多么容易。同樣的技術也適用于 Python dictionary,如清單 9 所示。
清單 9. 迭代和 dictionary
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> d = { 0 : 'zero' , 3 : 'a tuple' , 'two' : [ 0 , 1 , 2 ], 'one' : 1 } >>> for k in d.iterkeys(): ... print d[k] ... tuple [ 0 , 1 , 2 ] >>> for v in d.itervalues(): ... print v ... tuple [ 0 , 1 , 2 ] >>> for k, v in d.iteritems(): ... print 'd[' ,k, '] = ' ,v ... [ 0 ] = zero[ 3 ] = a tuple [ two ] = [ 0 , 1 , 2 ][ one ] = 1 |
這個示例演示了遍歷 dictionary 的三種方式:使用從 iterkeys()、itervalues() 或 iteritems() 方法返回的 Python 迭代器。(順便說一下,可以通過在 dictionary 上直接調用適當方法,比如 d.iterkeys(),從而檢查這些方法是否返回一個迭代器而不是容器數據類型。)iterkeys() 方法允許遍歷 dictionary 的鍵,而 itervalues() 方法允許遍歷 dictionary 包含的數據值。另一方面,iteritems() 方法允許同時遍歷鍵到數據值的映射。
dictionary:另一種強大的 Python 容器
本文討論了 Python dictionary 數據類型。dictionary 是一種異構的、易變的容器,依賴鍵到數據值的映射(而不是特定的數字次序)來訪問容器中的元素。訪問、添加和刪除 dictionary 中的元素都很簡單,而且 dictionary 很容易用于復合語句,比如 if 語句或 for 循環??梢栽?dictionary 中存儲所有不同類型的數據,可以按照名稱或其他復合鍵值(比如 tuple)訪問這些數據,所以 Python dictionary 使開發人員能夠編寫簡潔而又強大的編程語句。