流程的Python——字典

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所做的任何修改都會反饋到它上面

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章