1.在Python中標準庫中所有映射類型都是利用dict來實現的,因此它們有個共同的限制,即只有可散列 的數據類型才能用作這些映射的鍵,注意只有鍵有這個要求,值並不需要是可散列的數據類型。
在Python中原子不可變數據類型(str、bytes和數值類型)都是可散列類型,frozenset也是可散列類型,因爲根據其定義,frozenset裏只能容納可散列類型。元組的話,只有當一個元組裏包含的所有元素都是可散列類型的情況下,它纔是可散列的。
一般來講用戶自定義的類型的對象都是可散列的,散列值就是它們的id()函數的返回值。
2.字典的創建形式有如下幾種方法:
a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'one': 1, 'two': 2, 'three': 3})
print(a==b==c==d==e)
當然還可以利用字典推導來創建新的字典
DIAL_CODES = [
(86, 'China'),
(91, "India"),
(1, "United States"),
(62, 'Indonesia'),
(55, 'Brazil'),
(92, 'Pakistan'),
(880, "Bangladesh"),
(234, "Nigeria"),
(7, 'Russia'),
(81, 'Japan'),
]
country_code = {country: code for code, country in DIAL_CODES}
print(country_code)
code_country = {code: country.upper() for country, code in country_code.items() if code < 66}
print(code_country)
3.常見的映射方法
|
dict |
defaultdict |
OrederedDict |
|
---|---|---|---|---|
d.clear() |
√ |
√ |
√ |
移除所有元素 |
d.__contains__(k) |
√ |
√ |
√ |
檢查k是否在d中(k是鍵) |
d.copy() |
√ |
√ |
√ |
淺複製 |
d.__copy__() |
|
√ |
|
用於支持copy.copy |
d.default_factory |
|
√ |
|
在 __missing__ 函數中被調用的函數,用以給未找到的元素這隻值 |
d.__delitem__(k) |
√ |
√ |
√ |
del d[k] 移除鍵爲k的元素 |
d.fromkeys(it, [initial]) |
√ |
√ |
√ |
將迭代器it裏的元素設置爲映射裏的鍵,如果有initial參數,就把它作爲這些鍵對應的值。(默認是None) |
d.get(k, [default]) |
√ |
√ |
√ |
返回鍵k對應的值,如果字典裏沒有鍵k,則返回None或者default |
d.__getitem__(k) |
√ |
√ |
√ |
讓字典d能用d[k] 的形式返回鍵k對應的值 |
d.items() |
√ |
√ |
√ |
返回d裏所有的鍵值對 |
d.__iter__() |
√ |
√ |
√ |
獲取鍵的迭代器 |
d.keys() |
√ |
√ |
√ |
獲取所有鍵 |
d.__len__() |
√ |
√ |
√ |
可以用len(d) 的形式得到字典裏鍵值對的數量 |
d.__missing__(k) |
|
√ |
|
當 __getitem__ 找不到對應鍵的時候,這個方法會被調用 |
d.move_to_end(k, [last]) |
|
|
√ |
把鍵爲k的元素移動到最靠前或者最靠後的位置(last的默認值是True) |
d.pop(k, [default]) |
√ |
√ |
√ |
返回鍵k所對應的的值,然後移除這個鍵值對。如果沒有這個鍵,返回None或者default |
d.popitem() |
√ |
√ |
√ |
隨機返回一個鍵值對並從字典裏移除它 |
d.__reversed__() |
|
|
√ |
返回倒敘的鍵的迭代器 |
d.setdefault(k, [default]) |
√ |
√ |
√ |
若字典裏有鍵k,則直接返回k所對應的值;若無,則讓d[k]=default,然後返回default |
d.__setitem__(k, v) |
√ |
√ |
√ |
實現d[k]=v操作,把k對應的值設爲v |
d.update(m, [**kargs]) |
√ |
√ |
√ |
m可以是映射或者鍵值對迭代器,用來更新d裏對應的條目 |
d.values() |
√ |
√ |
√ |
返回字典裏的所有值 |
用setdefault處理找不到的鍵,示例如下:
"""創建一個從單詞到其出現情況的映射"""
import sys
import re
WORD_RE = re.compile(r'\w+')
index = {}
with open("xx.txt", encoding='UTF-8') as fp:
# enumerate(sequence, [start=0]) sequence -- 一個序列、迭代器或其他支持迭代對象。start -- 下標起始位置。
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start() + 1
location = (line_no, column_no)
# 下面是一種不好的實現
occurrences = index.get(word, [])
occurrences.append(location)
index[word] = occurrences
# 上面三行等價於下面一行
# 獲取單詞的出現情況列表,如果單詞不存在,把單詞和一個空列表放進映射,然後返回這個空列表
# 這樣就能在不進行第二次查找的情況下更新列表
index.setdefault(word, []).append(location)
# 以字母順序打印出結果
for word in sorted(index, key=str.upper):
print(word, index[word])
在上述的兩種實現中,二者的效果都是一樣的,只不過前者至少要進行兩側鍵查詢-如果鍵不存在則是三次,用setdefault只需要一次就可以完成整個操作。
4.映射的彈性鍵查詢
有時候爲了方便起見,就算某個鍵在映射裏不存在,我們也希望在通過這個鍵讀取值的時候能得到一個默認值。有兩種方法可以幫我們達到這個目的,一個是通過defaultdict這個類型而不是普通的dict, 另一個是自己定義一個dict的子類,然後在子類中實現__missing__方法。
方法一如下:
import sys
import re
import collections
WORD_RE = re.compile(r'\w+')
# 把list構造方法作爲default_factory來創建一個defaultdict
index = collections.defaultdict(list)
with open(sys.argv[1], encoding='UTF-8') as fp:
# enumerate(sequence, [start=0]) sequence -- 一個序列、迭代器或其他支持迭代對象。start -- 下標起始位置。
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start() + 1
location = (line_no, column_no)
# 如果index並沒有word的記錄,那麼default_factory會被調用,爲查詢不到的鍵創造一個值。
# 這個值在這裏是一個空列表,然後這個空列表會被賦值給index[word],繼而被當做返回值返回
# 因此,append(location)操作總能成功
index[word].append(location)
# 以字母順序打印出結果
for word in sorted(index, key=str.upper):
print(word, index[word])
在創建defaultdict對象的時候,我們需要給它配置一個爲找不到鍵創建默認值的方法。具體而言,在實例化一個defaultdict的時候,需要給構造方法提供一個可調用對象,這個可調用對象會在__getitem__碰到找不到的鍵的時候被調用,讓 __getitem__返回一個默認值。
比如在上面,我們在實例化defaultdict的時候,提供了list可調用對象,當 __getitem__找不到鍵的時候,會按一下步驟來走:
1.調用list()來創建一個新列表
2.把這個新列表作爲值,找不到的那個鍵作爲它的鍵,放入字典中
3.返回這個列表的引用
這個用來生成默認值的可調用對象存放在名爲default_factory的實例屬性中。如果在創建defaultdict的時候沒有指定default_factory,查詢不存在的鍵會觸發KeyError。
方法二(__missing__)如下:
class StrKeyDict0(dict):
def __missing__(self, key):
if isinstance(key, str): # 如果找不到的鍵本身就是字符串,那就拋出異常
raise KeyError(key)
return self[str(key)] # 如果找不到的鍵是字符串,那麼把它轉換成字符串再進行查找
def get(self, key, default=None):
try:
# get方法把查找工作用self[key]的形式委託給__getitem__,這樣在宣佈查找失敗之前
# 還能通過 __missing__ 再給某個鍵一個機會
return self[key]
except KeyError:
return default
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys()
if __name__ == "__main__":
d = StrKeyDict0([('2', 'two'), ('4', 'four')])
print(d[2])
print(d['2'])
print(d.get('2'))
print(d.get(2))
print(d.get(1, '不存在'))
注意:__missing__方法只會被__getitem__調用(比如在表達式d[k]中)。像 k in dict.keys() 這種操作在Python3中是很快的,而且即便映射類型對象很龐大也沒關係,因爲dict.keys()返回的值是一個“視圖”。視圖就像一個集合,而且跟字典類似的是,在視圖裏查找一個元素的速度很快。
Collections.Counter 一個很有用的映射類型,這個類型會給鍵準備一個整數計數器。每次更新一個鍵的時候都會增加這個計數器。所以這個類型可以用來給可散列對象技術,或者是當做多重集來用——對重集合就是集合裏的元素可以出現不止一次。
ct = collections.Counter(["Hello", "sss", "sss", "sss", "hello"])
print(ct)
5.不可變映射類型
從Python3.3開始,types模塊引入了一個封裝類名叫MappingProxyType。如果給這個類一個映射,它會返回一個只讀的映射視圖。雖然是個只讀的視圖,但是它是動態的,即如果原映射出現了改動,那麼我們可以通過這個視圖觀察到,但是無法通過這個視圖對原映射進行修改。
from types import MappingProxyType
d = {1: 'A'}
d_proxy = MappingProxyType(d)
print(d_proxy) # d中的內容可以通過d_proxy看到
# d_proxy[1] = 'X' # 但是通過d_proxy並不能做任何修改
print(d_proxy)
d[1] = 'X'
print(d_proxy) # d_proxy是動態的,即對d所做的任何修改都會反饋到它上面