python高級(三)—— 字典和集合(泛映射類型)

来源:https://www.cnblogs.com/liao-sir/archive/2018/02/27/8470636.html
-Advertisement-
Play Games

本文主要內容 可散列類型 泛映射類型 字典 (1)字典推導式 (2)處理不存在的鍵 集合 映射的再討論 python高級——目錄 文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級 可散列類型 泛映射類型 字典 ...


本文主要內容

可散列類型

泛映射類型

字典

    (1)字典推導式

  (2)處理不存在的鍵

    (3)字典的變種

集合

映射的再討論

 

python高級——目錄

文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級

 

可散列類型

'''
    可散列數據類型(也稱可hash)————我理解"可散列"就是"可hash"
    可hash的對象需要實現__hash__方法,返回hash值;另外為了與其他對象比較還需要有__eq__方法

    原子不可變數據類型(str、bytes和數值類型)都是可散列的,可散列對象必須滿足下列要求:
    (1)實現了__hash__方法,並且所得到的hash值是不變的
    (2)實現了__eq__方法,用來比較
    (3)若a == b 為真,那麼hash(a) == hash(b)也是真
'''


# 創建類Foo,並實現__hash__和__eq__

class Foo:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        print("正在hash...")
        return hash(self.name)

    def __eq__(self, other):
        print("正在比較...")
        return self.name == other.name

    def __repr__(self):
        return self.name


if __name__ == "__main__":

    f1 = Foo("小李")
    f2 = Foo("小紅")
    f3 = Foo("小李")

    s = set([f1, f2, f3])        # 集合實現不重覆的原理正好利用了散列表
    print(s)                     # {小紅, 小李}
    print( f1 == f3, hash(f1) == hash(f3))      # True True 滿足可散列對象的第三個條件
'''
    對於元組來說,只有當一個元組包含的所有元素都是可hash的情況下,它才是可hash的
'''
t1 = (1, 2, 3, [1, 2])   # 元組裡的列表的值是可變的,所以不可hash
try:
    print(hash(t1))
except Exception as e:
    print(e)             # unhashable type: 'list'

t2 = (1, 2, 3, (1, 2))   # 元組裡的元素都是不可變的,並且第二層元組裡面的元素也不可變,所以可hash
print(hash(t2))          # 3896079550788208169

t3 = (1, 2, 3, frozenset([1, 2]))
print(hash(t3))          # -5691000848003037416

 

泛映射類型

'''
    泛映射類型就是廣義上的對應關係,在數學中,我們將集合A對應集合B中的對應法則稱為"映射"(Mapping)
    同樣,在python里,我們稱"鍵值對"為映射,這其實也是一種對應法則
    如果一個數據類型是映射,那麼它肯定屬於collections.abc.Mapping,可使用isinstance函數測試

    PS: 字典是 Python 語言中唯一的映射類型。映射類型對象里哈希值(鍵) 和指向的對象(值)是一對多的關係。
'''

from collections import abc

# 我們測試一些常用的類型是不是映射
if __name__ == "__main__":
    print(isinstance({}, abc.Mapping))      # True   字典是典型的鍵值對
    print(isinstance([1, 2], abc.Mapping))  # False  列表是序列
    print(isinstance((1, 2), abc.Mapping))  # False  元組是序列
    print(isinstance('adfasfd', abc.Mapping))  # False  字元串也是序列
'''
   大家可以查看_collections_abc.py源代碼,裡面基本的類型包含:
    ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
    "Hashable", "Iterable", "Iterator", "Generator",
    "Sized", "Container", "Callable",
     "Set", "MutableSet",
     "Mapping", "MutableMapping",
     "MappingView", "KeysView", "ItemsView", "ValuesView",
     "Sequence", "MutableSequence",
    "ByteString",
    ]
'''

 

'''
    如果我們自己想定義一個映射類型的對象,那麼必須實現__getitem__、__iter__、__len__方法
    
    PS:關於該部分的原理,本人暫未查看說明文檔,畢竟現實中幾乎不可能自定義映射;有興趣的同志可深入鑽研。
'''


class Foo(abc.Mapping):
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        return self.name

    def __iter__(self):
        return iter(str(self.name))

    def __len__(self):
        return len(self.name)


print(isinstance(Foo("123"), abc.Mapping))      # True

 

字典

'''
    字典是python內置類型中唯一的映射,先看創建字典的幾種方法

    1、對象創建
    2、大括弧
    3、zip
'''

if __name__ == "__main__":
    # 1、利用實例化對象的方法創建
    a = dict(key1=1, key2=2, all=[1, 2, 3])
    b = dict([('key3', 3), ('key4', 4)])
    c = dict({"key5": 5, "key6": 6})

    print("a:", a)     # a: {'key1': 1, 'all': [1, 2, 3], 'key2': 2}
    print("b:", b)     # b: {'key3': 3, 'key4': 4}
    print("c:", c)     # c: {'key6': 6, 'key5': 5}

    # 2、直接使用大括弧
    d = {"key7": 7, "key8": 8}
    print("d:", d)     # d: {'key8': 8, 'key7': 7}

    # 3、使用zip
    e = dict(zip(("key9", "key10", "key11"), [9, 10, 11]))
    print("e:", e)     # e: {'key11': 11, 'key10': 10, 'key9': 9}

(1)字典推導式

 

'''
    字典推導式:字典推導式的創建方法同列表推導式類似

    以下直接引用《流暢的python》中的例子
'''


if __name__ == "__main__":
    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)   # {'Russia': 7, 'Indonesia': 62, 'Brazil': 55, 'China': 86, 'India': 91, 'Bangladesh': 880, 'Pakistan': 92, 'United States': 1, 'Nigeria': 234, 'Japan': 81}

    code_upper = {code: country.upper() for country, code in country_code.items() if code < 66}
    print(code_upper)     # {1: 'UNITED STATES', 7: 'RUSSIA', 62: 'INDONESIA', 55: 'BRAZIL'}
(2)處理不存在的鍵
'''
    處理找不到的鍵

    在實際場景中,當使用d[key]的方法查找數據的時候,如果找不到該鍵,python會拋出KeyError異常;
    如果是取值操作,可以使用d.get(key, default)來解決,可以給找不到的鍵一個預設的值
    但是如果要給更新某個不存在鍵對應的值的時候,就稍顯麻煩了,可以使用以下方法解決:
        1、用setdefault處理dict找不到的鍵
        2、使用defaultdict對象
        3、__missing__方法
'''

class Foo:
    def __init__(self, name=None):
        self.name = name

    def __repr__(self):
        return str(self.name)

    def setattr(self, key, value):
        self.__setattr__(key, value)
        return self


if __name__ == "__main__":
    d1 = {}
    print(d1.get("key", "default"))   # default   使用d.get(key, default)的方法取值


    # 1、用setdefault處理dict找不到的鍵
    d2 = {}
    d2.setdefault("key", [x for x in "adfaf"])  # setdefault雖然是set名字,但是是取值操作,只有當鍵不存在時才進行賦值,並返回該值
    l = d2.setdefault("key", [])
    print(l)                                    # ['a', 'd', 'f', 'a', 'f']

    d2.setdefault("key2", []).extend([1, 2, 3]) # 返回空列表,所以可在後面直接使用方法extend
    print(d2)                                   # {'key': 'default', 'key2': [1, 2, 3]}

    # 2、使用defaultdict對象
    #  在python中,還有一些dict的變種類型,defaultdict為其中一種,位於collections中
    from collections import defaultdict

    dic = defaultdict(list)                    # 將list的構造方法作為default_factory(只有__getitem__找不到值時調用)
    dic["key"].extend([1, 2, 3])               # dic中不含有"key"鍵,此時default_factory會被調用,創造一個空列表,並連接[1, 2, 3]
    print(dic["key"])                # [1, 2, 3]

    dic = defaultdict(Foo)           # 將Foo的構造方法作為default_factory創建一個defaultdict
    print(dic["key"].setattr("name", "default"))                # default

    # 3、__missing__方法
    # 所有的映射類型在找不到鍵的時候,都會牽扯到__missing__方法;如果在__getitem__找不到鍵的時候,python就會自動調用它
    # 另外,__missing__方法只會被getitem調用,對get或者__contains__沒有影響

    class My_dict(dict):
        def __missing__(self, key):
            print("正在調用__missing__...")

    mdict = My_dict(one=1, two=2, three=3)
    print(mdict)     # {'two': 2, 'three': 3, 'one': 1}
    mdict["key"]     # 正在調用__missing__...
(3)字典的變種
'''
    在python中雖然只有dict為映射類型,但是dict有很多變種,上面defaultdict就是,除此之外還有:

    (1)OrderedDict: 有順序的字典
     (2) ChainMap: 可以容納數個不同的映射對象
     (3) Counter:  給鍵準備一個整數計數器,每次更新鍵的時候會增加該計數器
    (4)UserDict:  將標準的dict用python實現了一遍
'''


from collections import OrderedDict, ChainMap, Counter, UserDict

if __name__ == "__main__":
    # 1、OrderedDict
    d = OrderedDict()
    d['one'] = 1
    d['two'] = 2
    d['three'] = 3
    for _ in range(10):
        print("%d次:" % _)
        for k, v in d.items():
            print("**", k, v)        # OrderedDict迭代的時候的順序總是跟插入順序一致


    # 2、ChainMap

    pylookup = ChainMap(d, globals())   # d和globals()都是映射類型,ChainMap會將其組合
    for v, k in pylookup.items():
        print(v, k)

    # 3、Counter
    ct = Counter('asfjlajslfjals')
    print(ct)      # Counter({'j': 3, 'l': 3, 's': 3, 'a': 3, 'f': 2})
                   # 存儲的是每個字母出現的次數
    ct.update('jjjjjjjjlllllllll')
    print(ct)      # # Counter({'l': 12, 'j': 11, 's': 3, 'a': 3, 'f': 2})

    import random
    ct2 = Counter([random.randrange(1, 5) for _ in range(100)])   # 列表推導式創建Counter
    print(ct2)     # Counter({1: 30, 2: 24, 4: 24, 3: 22})

    ct3 = Counter((random.randrange(1, 5) for _ in range(100)))   # 生成器創建Counter
    print(ct3)      # Counter({2: 40, 3: 23, 4: 20, 1: 17})

    class Foo:
        def __init__(self, num):
            self.l = [random.randrange(1, 5) for _ in range(num)]

        def __iter__(self):
            return iter(self.l)

    ct4 = Counter(Foo(100))            # 可迭代對象創建Counter
    print(ct4)      # Counter({2: 31, 3: 25, 4: 25, 1: 19})

    # 4、UserDict
    # 創建自定義的映射類型,一般以UserDict為基類

    class My_dict(UserDict):
        def __missing__(self, key):
            if isinstance(key, str):
                raise KeyError(key)
            return self[str(key)]

        def __contains__(self, key):
            return str(key) in self.data

        def __setitem__(self, key, item):
            print("調用__setitem__。。。")
            self.data[str(key)] = item

    mdict = My_dict()
    mdict["one"] = 1      # 調用__setitem__。。。(下同)
    mdict["two"] = 2
    mdict["three"] = 3
    print(mdict)   # {'three': 3, 'one': 1, 'two': 2}

 

集合

'''
    集合對於很多人並不陌生,中學階段就已經接觸過。集合具有:
    (1)確定性:每一個對象都能確定是不是某一集合的元素,沒有確定性就不能成為集合
    (2)互異性:集合中任意兩個元素都是不同的對象
    (3)無序性:{a,b,c}{c,b,a}是同一個集合

    在python中,set中的元素必須是可散列的,但set本身不可散列(但是frosenset是可散列的)


    另外:set實現了很多基礎運算
    &(交集)、|(並集)、-(差集)
'''


if __name__ == "__main__":
    # 創建集合
    s1 = set([1, 2, 3])
    s2 = {1, 2, 3, 4}
    print(s1, s2)     # {1, 2, 3} {1, 2, 3, 4}

    # 集合推導式
    s3 = {x**2 for x in range(10)}
    print(s3)         # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}

 

set的操作方法很多,本文截自<流暢的python>一書,如下三個表:

表一:集合的數學方法

 

表2:集合的比較運算

 

表3:集合的其他運算

 

映射的再討論 

'''
    python標準庫裡面的映射類型都是可變的,有時候需要使用不可變的映射,從python3.3開始,types模塊中引入了
    MappingProxyType類,如果給這個類一個映射,那麼它會返回這個映射的試圖,該試圖是動態的,原映射如果有改動
    可立即通過這個試圖觀察到,但是這個試圖無法對該映射進行修改。
'''
from types import MappingProxyType

if __name__ == "__main__":
    d = {'one':1, 'two':2, 'three':3}
    d_proxy = MappingProxyType(d)
    print(d_proxy)     # {'three': 3, 'two': 2, 'one': 1}
    print(d_proxy['one'])  # 1
    for k, v in d_proxy.items():
        print(k, v)

    #d_proxy['four'] = 4   # 報錯:TypeError: 'mappingproxy' object does not support item assignment
    d['four'] = 4
    print(d_proxy)     # {'two': 2, 'three': 3, 'four': 4, 'one': 1}

 

  另外,《流暢的python》77頁到80頁對散列表演算法以及字典、集合的效率、平時需要註意的問題進行了比較詳細的探討,建議嚴謹並有興趣的同仁閱讀,該部分內容對理解字典類型無比有益,場景中捉摸不透的莫名其妙的bug可能會迎刃而解。

   重要的結論摘錄如下:

  (1)鍵必須是可散列的

  (2)字典在記憶體上的開銷巨大

  (3)鍵查詢很快

  (4)鍵的次序取決於添加順序

  (5)往字典里添加新鍵可能會改變已有鍵的順序

 

python高級系列文章目錄

python高級——目錄

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Learning to Rank,即排序學習,簡稱為 L2R,它是構建排序模型的機器學習方法,在信息檢索、自然語言處理、數據挖掘等場景中具有重要的作用。其達到的效果是:給定一組文檔,對任意查詢請求給出反映文檔相關性的文檔排序。本文簡單介紹一下 L2R 的基本演算法及評價指標。 背景 隨著互聯網的快速發 ...
  • Description Siruseri 城中的道路都是單向的。不同的道路由路口連接。按照法律的規定, 在每個路口都設立了一個 Siruser i 銀行的 ATM 取款機。令人奇怪的是,Siruseri 的酒吧也都設在路口,雖然並不是每個路口都設有酒吧。Bandit ji 計劃實施 Siruseri ...
  • Semaphore(信號量)是JUC包中比較常用到的一個類,它是AQS共用模式的一個應用,可以允許多個線程同時對共用資源進行操作,並且可以有效的控制併發數,利用它可以很好的實現流量控制。Semaphore提供了一個許可證的概念,可以把這個許可證看作公車車票,只有成功獲取車票的人才能夠上車,並且車 ...
  • http://www.lydsy.com/JudgeOnline/problem.php?id=2323 根本想不到... 方法: get(i,j)表示第i到j個數字拼起來組成的數字ans[i][0/1]表示第一次分裂中,第i個數字之後斷開,前i個數字第二次分裂後形成的最後一個二次分裂體否/是與其之 ...
  • 緩存 前言: 大家都聽過緩存,緩存是幹啥的呢?我們可以和json和pickle來說,兩個程式之間實現信息交互,可以通過在A程式中把數據改成json ,然後傳給B程式,通過文件這個介質。文件這個效率很低。 比方說一個是QQ,一個是微信,我們想要實現二者的交互,我們之前學了rabbitMQ,可以實現消息 ...
  • 作為程式員,要時刻保持一顆好奇心和想要學習的姿態。 練習怎樣利用搜狗微信爬取某指定微信公眾號的歷史文章。爬取微信公眾號本身難度非常大,感謝搜狗提供了一個可以爬取數據的平臺。 代碼部分參考於: https://github.com/Chyroc/WechatSogou/tree/master/wec ...
  • 遇到一個奇葩的需求。一般情況下我們列印單據,用FastReport設置列印格式,也就是就設一個模版頁而己,就是一種單據格式。如果列印的單據數據多了就自動列印多頁了,他們的格式是一樣的。也就是讀同一個模版頁。 現的需求是,如果列印N頁內容。每一頁的格式除了表體外是一樣的(也可能部份不同)。而表體取自不 ...
  • ==>(個人微信公眾號:IT知更鳥)歡迎關註<^>@<^> 1.問題描述: 金字塔5層,從上到下,星號數1、3、5、7、9,空格數4、3、2、1 。 2.問題分析 : 1)確定程式框架: 用for迴圈。 2)尋找空格和星號的規律: 空格數 5-行數;依次遞減1。 星號數 行數*2-1;依次遞增2。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...