一、概述
映射類型(Mapping Types)是一種關聯式的容器類型,它存儲了對象與對象之間的映射關系。
字典(dict)是Python中唯一的映射類型,它是存儲了一個個 鍵值對(由 鍵 映射到 值)的關聯容器。其中,鍵(key)必須是可哈希的Python對象,而 值(value)可以是任何Python對象。在功能上,Python中的字典類似於C++中的map。
Python中最強大、最靈活的數據類型當屬 列表 和 字典,以下是對這兩種數據類型的簡單比較:
比較點 | 列表 | 字典 |
---|---|---|
表示方法 | [],[1, 2] | {},{'a': 1, 'b': 2} |
訪問元素的方式 | 索引 | 鍵 |
有序性 | 有序 | 無序 |
可變性 | 可變 | 可變 |
可操作性 | 操作豐富 | 操作豐富 |
表征的數據結構 | 數組、堆棧、隊列等 | 哈希表等 |
二、操作
字典支持的主要操作如下:
操作 | 說明 |
---|---|
class dict(other) | 創建字典(other可以是字典、(key, value)對的迭代器或關鍵字參數) |
dict.fromkeys(seq[, value]) | 創建字典:用序列seq中的元素作為鍵,值全為value(未指定,則默認為None) |
len(d) | 返回字典d的長度(即d中元素的個數) |
d[key] | 如果鍵key在字典d中,則返回其中key對應的值;否則拋出KeyError異常 |
d[key] = value | 設置d[key]的值為value(存在則修改,不存在則添加) |
del d[key] | 如果鍵key在字典d中,則從字典d中刪除d[key];否則拋出KeyError異常 |
key in d | 如果key在字典d中,返回True;否則,返回False |
key not in d | 如果key在字典d中,返回False;否則,返回True |
iter(d) | 同iterkeys() |
d.clear() | 刪除字典d中的所有元素 |
d.copy() | 返回字典d的淺拷貝 |
d.get(key[, default]) | 如果key在字典d中,則返回d[key];否則返回default(未指定,則默認為None) |
d.has_key(key) | 同key in d(推薦使用key in d) |
d.items() | 返回包含字典d中的(key, value)對的列表 |
d.iteritems() | 迭代版的items():返回迭代器 |
d.iterkeys() | 迭代版的keys():返回迭代器 |
d.itervalues() | 迭代版的values():返回迭代器 |
d.keys() | 返回包含字典d中的鍵的列表 |
d.pop(key[, default]) | 如果key在字典d中,則返回並刪除d[key];否則返回default(未指定,則拋出KeyError異常) |
d.popitem() | 返回並刪除字典d中的任意一個元素(如果d為空,則拋出KeyError異常) |
d.setdefault(key[, default]) | 如果key在字典d中,則返回d[key];否則執行d[key] = default,並返回default(未指定,則默認為None) |
d.update([other]) | 將other中的(key, value)對添加到字典d中(other可以是字典、(key, value)對的迭代器或關鍵字參數) |
d.values() | 返回包含字典d中的值的列表 |
d.viewitems() | 返回字典d的元素視圖 |
d.viewkeys() | 返回字典d的鍵視圖 |
d.viewvalues() | 返回字典d的值視圖 |
以上操作的示例如下:
>>> a = {'one': 1, 'two': 2, 'three': 3}
>>> b = dict(one=1, two=2, three=3)
>>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d
True
>>> d = dict.fromkeys(['a', 'b', 'c'])
>>> d
{'a': None, 'c': None, 'b': None}
>>> d = dict.fromkeys(['a', 'b', 'c'], 6)
>>> d
{'a': 6, 'c': 6, 'b': 6}
>>> len(d)
3
>>> d.clear()
>>> d
{}
>>> d = a.copy()
>>> d
{'one': 1, 'three': 3, 'two': 2}
>>> d['three']
3
>>> d['four'] = 4
>>> d
{'four': 4, 'one': 1, 'three': 3, 'two': 2}
>>> del d['one']
>>> d
{'four': 4, 'three': 3, 'two': 2}
>>> 'four' in d, 'four' not in d
(True, False)
>>> d.has_key('four')
True
>>> d.get('one'), d.get('one', 10)
(None, 10)
>>> for k in d:
... print k,
...
four three two
>>> for k in iter(d):
... print k,
...
four three two
>>> for k in d.keys():
... print k,
...
four three two
>>> for k in d.iterkeys():
... print k,
...
four three two
>>> for k in d.viewkeys():
... print k,
...
four three two
>>> for v in d.values():
... print v,
...
4 3 2
>>> for v in d.itervalues():
... print v,
...
4 3 2
>>> for v in d.viewvalues():
... print v,
...
4 3 2
>>> for i in d.items():
... print i,
...
('four', 4) ('three', 3) ('two', 2)
>>> for i in d.iteritems():
... print i,
...
('four', 4) ('three', 3) ('two', 2)
>>> for i in d.viewitems():
... print i,
...
('four', 4) ('three', 3) ('two', 2)
>>> d.setdefault('two')
2
>>> d
{'four': 4, 'three': 3, 'two': 2}
>>> d.setdefault('one', 1)
1
>>> d
{'four': 4, 'one': 1, 'three': 3, 'two': 2}
>>> d.update(five=1)
>>> d
{'four': 4, 'one': 1, 'five': 1, 'three': 3, 'two': 2}
>>> d.update({'six': 6})
>>> d
{'four': 4, 'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
>>> d.pop('four')
4
>>> d
{'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
>>> d.popitem()
('five', 1)
>>> d
{'two': 2, 'six': 6, 'three': 3, 'one': 1}
三、字典特性
1、有序與無序
從概念上講,字典提供了這樣一種抽象:容器中的元素之間完全獨立(於是也沒有先后順序),“鍵”是訪問元素的唯一方式。在這種 抽象層面 上,字典是 無序 的。
從實現上講,字典其實是由 哈希表 實現的。而哈希表的基本思想是:通過 哈希函數(hash function)將“鍵”轉換為“索引”,再使用“索引”去訪問 連續列表(如C中的數組)中的元素。由此可知,在哈希表中:一方面,元素本質上是存儲在一個連續列表中的,因此是 有序 的;另一方面,用戶無法確定元素在連續列表中的實際位置(只能使用“鍵”去訪問元素,而“鍵”與“索引”的映射關系是由哈希函數在內部指定的),因此又是 無序 的。
因此在 實現層面 上,字典同時具備了 無序 和 有序 的特點:
- 無序體現在:字典中元素的排列順序與添加順序無關
- 有序體現在:在字典保持不變的情況下,字典中元素的排列順序是固定的
上圖對應的示例如下:
# 無序
>>> d = {}
>>> d['a'] = 1
>>> d
{'a': 1}
>>> d['b'] = 2
>>> d
{'a': 1, 'b': 2}
>>> d['c'] = 3
>>> d
{'a': 1, 'c': 3, 'b': 2}
# 有序
>>> for k in d: # 鍵的順序固定
... print k,
...
a c b
>>> for v in d.values(): # 值的順序固定
... print v,
...
1 3 2
>>> for i in d.items(): # 元素的順序固定
... print i,
...
('a', 1) ('c', 3) ('b', 2)
2、字典的鍵
字典的鍵具有以下特性:
1)可哈希的(hashable)
只有 可哈希的 對象才能作為字典的鍵,一個可哈希的對象必須滿足以下兩個條件:
- 該對象在其生命周期內有一個不變的哈希值(需要實現
__hash__()
方法) - 該對象是可比較的(需要實現
__eq__()
或__cmp__()
方法)
Python中可哈希的對象有:
- 數值、字符串,以及只含有數值或字符串的元組
- 用戶自定義類的實例(默認是可哈希的,也可以通過實現
__hash__()
和__cmp__()
來修改默認行為)
2)哈希等價鍵
假設有字典d的兩個鍵:keyA和keyB,我們稱keyA和keyB是 哈希等價鍵(自己杜撰的名詞),如果keyA和keyB滿足以下兩個條件:
- hash(keyA) == hash(keyB)
- cmp(keyA, keyB) == 0
如果keyA和keyB是哈希等價鍵,那么它們將被視為完全相同的兩個鍵,於是d[keyA]和d[keyB]會指向同一個字典元素。
例如,1和1.0就滿足上述兩個條件,因此是哈希等價鍵:
>>> hash(1), hash(1.0)
(1, 1)
>>> cmp(1, 1.0)
0
>>> d = {}
>>> d[1] = 'int 1'
>>> d
{1: 'int 1'}
>>> d[1.0] = 'float 1'
>>> d
{1: 'float 1'}
對於用戶自定義的類實例,默認情況下(即沒有實現__hash__()
和__cmp__()
時),hash(...)和cmp(...)的結果與 id() 有關(參考 hashable 和 __cmp__())。默認情況下,一個自定義類的任意兩個實例都不是哈希等價鍵:
>>> class A: pass
...
>>> a1 = A()
>>> a2 = A()
>>> hash(a1), hash(a2)
(-1064359592, -1064359600)
>>> cmp(a1, a2)
1
>>> d = {}
>>> d[a1] = 'a1'
>>> d
{<__main__.A instance at 0x8f2958c>: 'a1'}
>>> d[a2] = 'a2'
>>> d
{<__main__.A instance at 0x8f2958c>: 'a1', <__main__.A instance at 0x8f2950c>: 'a2'}
如果想要讓同一個類的任意兩個實例都是哈希等價鍵,則可以參考以下示例:
>>> class A:
... def __hash__(self):
... return hash(A)
... def __cmp__(self, other):
... return cmp(self.__hash__(), other.__hash__())
...
>>> a1 = A()
>>> a2 = A()
>>> hash(a1), hash(a2)
(-1064346499, -1064346499)
>>> cmp(a1, a2)
0
>>> d = {}
>>> d[a1] = 'a1'
>>> d
{<__main__.A instance at 0x8f64a4c>: 'a1'}
>>> d[a2] = 'a2'
>>> d
{<__main__.A instance at 0x8f64a4c>: 'a2'}
類似地,如果想要讓一個類的任意一個實例與整數1成為哈希等價鍵,則可以按照以下方式實現:
>>> class A:
... def __hash__(self):
... return 1
... def __cmp__(self, other):
... return cmp(self.__hash__(), other.__hash__())
...
>>> a = A()
>>> hash(1), hash(a)
(1, 1)
>>> cmp(1, a)
0
>>> d = {}
>>> d[1] = 'int 1'
>>> d
{1: 'int 1'}
>>> d[a] = 'instance a'
>>> d
{1: 'instance a'}
四、字典視圖
從2.7版本開始,Python中引入了字典視圖(Dictionary views)。字典視圖 是字典的 動態視圖:它們會與字典保持同步,實時反應出字典的變化。字典視圖共有3種:鍵視圖(Keys views)、值視圖(Values views)和 元素視圖(Items views),它們分別由dict.viewkeys()、dict.viewvalues()和dict.viewitems()三個函數返回。
所有的字典視圖都支持以下操作:
操作 | 說明 |
---|---|
len(dictview) | 返回字典的長度 |
iter(dictview) | 返回(鍵、值、元素)迭代器 |
x in dictview | 判斷x是否為(鍵、值、元素)的成員 |
此外,因為字典的鍵是 唯一 且 可哈希的,所以 鍵視圖 還支持 類似集合(set-like)的操作。如果字典的值是 可哈希的,那么 元素視圖 也支持這些操作:
操作 | 說明 |
---|---|
dictview & other | 求交集 |
dictview | other | 求並集 |
dictview - other | 求差集 |
dictview ^ other | 求對稱差集 |
關於字典視圖的示例,請參考 Dictionary view objects。
五、應用
1、模擬switch-case語句
以下是C中一個使用switch-case語句的示例:
int select(char c)
{
int num = -1;
switch (c)
{
case 'a':
num = 1;
break;
case 'b':
num = 2;
break;
case 'c':
num = 3;
break;
default:
num = 0;
break;
}
return num;
}
Python中沒有提供switch-case語句,但使用字典可以輕松實現類似上面的功能:
d = {'a': 1, 'b': 2, 'c': 3}
# 普通版本
def select1(c):
num = -1
if c not in d:
num = 0
else:
num = d[c]
return num
# 驚呆版本
def select2(c):
return d.get(c, 0)
2、稀疏矩陣
使用元組作為字典的鍵,可以構建類似稀疏矩陣的數據結構:
>>> matrix = {}
>>> matrix[(2, 3, 4)] = 88
>>> matrix[(7, 8, 9)] = 99
>>>
>>> matrix
{(2, 3, 4): 88, (7, 8, 9): 99}
>>>
>>> x, y, z = 2, 3, 4
>>> matrix[(x, y, z)]
88
3、符號表
實際上,Python本身就在內部大量使用了字典,一個典型的應用就是符號表:
>>> locals() # 局部符號表
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
>>> globals() # 全局符號表
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}