Python基礎:映射(字典)


一、概述

映射類型(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}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM