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 部分
>>> 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 的幫助
>>> 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]
…[/code]關於 dict 類的幫助指出,可以使用構造函數直接創建 dictionary,而不使用花括號。既然與其他容器數據類型相比,在創建 dictionary 時必須提供更多的數據,那麽這些創建方法比較復雜也就不足為奇了。但是,在實踐中使用 dictionary 並不難,如清單 3 所示。
清單 3. 在 Python 中創建 dictionary,第 2 部分
>>> 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 中的元素
>>> 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
>>> 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. 異構的容器
>>> 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. 壹般關系操作符
>>> 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
>>> 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() 方法所返回的鍵值列表中。可以使用相似的操作檢查某個值是否在調用 values() 方法所返回的數據值列表中。但是,可以使用 dictionary 名作為簡寫表示法。這是有意義的,因為壹般希望知道某個數據值(而不是鍵值)是否在 dictionary 中。
在 “Discover Python, Part 6” 中,您看到了使用 for 循環遍歷容器中的元素是多麽容易。同樣的技術也適用於 Python dictionary,如清單 9 所示。
清單 9. 叠代和 dictionary
>>> 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 使開發人員能夠編寫簡潔而又強大的編程語句。