導語:本文章記錄了本人在學習Python基礎之數據結構篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。
本文重點:html
一、掌握常見的字典建立,查詢,判別方法;
二、瞭解字典中的defaultdict、子類化Userdict和常見映射類型;
三、瞭解支撐字典和集合背後的散列表的工做原理。
分爲字面量句法和構造方法兩種,下面以{"one":1,"two":2,"three":3}爲例java
d1={"one":1,"two":2,"three":3}#字面量句法 d2=dict(one=1,two=2,three=3) d3=dict([("one",1),("two",2),("three",3)]) d4=dict({"one":1,"two":2,"three":3}) d5=dict(zip(["one","two","three"],[1,2,3]))#zip並行解包 print(d1==d2==d3==d4==d5)#True
以上五種方法建立的字典是相等的。python
映射類型(Mapping Types)是一種關聯式的容器類型,它存儲了對象與對象之間的映射關係。
字典是Python中惟一的映射類型,它是存儲了若干鍵值對(由鍵映射到值)的關聯容器。算法
collections.abc模塊中有兩個抽象基類,分別是Mapping和MutableMapping,它們爲dict和其餘相似的類型定義形式接口。安全
isinstance:斷定object的類型
語法:isinstance(object, classinfo)數據結構
eg:app
from _collections_abc import Mapping my_dict={} print(isinstance(my_dict,Mapping))#判斷數據是否爲廣義映射類型。輸出True.
isinstance和type的區別:
若對象是classinfo中一個類的子類,isinstance能夠判斷出來返回True,而type是不能的。函數
字典推導:在{}中使用命令語句加for甚至if實現迭代推導出新列表的操做。
post
Country_Codes=[(86,"China"),(91,"India"),(1,"United States"),(62,"Indonesia"),(55,"Brazil"),(92,"Pakistan"),(81,"Japan")] dict1={country:code for code,country in Country_Codes}#推導過程 print(dict1) dict2={code:country.upper() for code,country in Country_Codes if code>80}#由限制要求建立字典 print(dict2) #輸出: {'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62, 'Brazil': 55, 'Pakistan': 92, 'Japan': 81} {86: 'CHINA', 91: 'INDIA', 92: 'PAKISTAN', 81: 'JAPAN'}
d.setdefault VS d.get
d.setdefault(k,[default])和d.get(k,[default])兩種方法均可以處理找不到的鍵的狀況,區別在於setdefault在返回默認值的同時可以在原字典建立新的k-default鍵值對。
因此更新某個鍵值對但鍵不必定存在時,用d.setdefault更好一些.學習
eg1:處理找不到的鍵
names=["Ailee","Bob","Cindy"] ages=["19","17","15"] dict3={x:y for x,y in zip(names,ages)}#用zip能夠並行拆包. print(dict3) print(dict3.get("David","20")) print(dict3)#get處理查不到的鍵時返回默認值,但不會在原字典建立這個鍵. dict3.setdefault("David","20") print(dict3)#setdefault處理查不到的鍵時返回默認值,而且會在原字典建立這個鍵.
格式:class collections.defaultdict([default_factory[, ...]])
defaultdict是內建dict的子類,它可以在查詢找不到的鍵時爲其創造默認值,由此避免拋出keyerror。其餘功能與dict相同。
eg:defaultdict推導
from _collections import defaultdict dict3=defaultdict(list,[(x,y) for x,y in zip([1,2,3,4,5],list("apple"))]) print(dict3) #輸出: defaultdict(<class 'list'>, {1: 'a', 2: 'p', 3: 'p', 4: 'l', 5: 'e'})
eg:查詢點名冊同窗的出席次數
from _collections import defaultdict namelist=['Ailee', 'Bob', 'Cindy', 'Ailee', 'Bob', 'Cindy', 'Cindy', 'Cindy', 'Bob', 'Cindy', 'Ailee', 'Bob', 'Bob'] count=defaultdict(int)#使用記錄值數據結構整型做爲默認的工廠函數 for x in namelist: count[x]+=1 print(count)#defaultdict(<class 'int'>, {'Ailee': 3, 'Bob': 5, 'Cindy': 5})
原理解釋:defaultdict在查詢找不到的鍵時會經過__getitem__調用__missing__,而後__missing__根據default_factory選擇返回默認值。當不輸入default_factory時,會拋出keyerror。
咱們能夠經過print (defaultdict.__missing__.__doc__)來看__missing__的內部實現:
__missing__(key) # Called by __getitem__ for missing key; pseudo-code: if self.default_factory is None: raise KeyError((key,)) self[key] = value = self.default_factory()#爲找不到的鍵建立默認值 return value
注意:__missing__只能被__getitem__調用,調用__getitem__可用d[k],d.get(k)無效。
default_factory的選擇
可調用函數做爲初始化函數參數
使用任何不帶參數的可調用函數,並以該函數返回值做爲默認值。
仍以點名code爲例,有兩種方法:
1)自定義函數:
def zero(): return 0 count=defaultdict(zero)
2)使用lambda建立匿名函數
count=defaultdict(lambda :0)
UserDict繼承自抽象基類(abstract based class)中的MutableMapping。UserDict是讓用戶繼承寫子類的。
之因此傾向於從UserDict而不是dict繼承的緣由是,這是由於在覆蓋重寫dict類的 get(k, default)、__setitem__( )、__contain__( )、__missing__( ) 等方法時,經常又會使用到 mapObj[k]、 k in mapObj、mapObj[k] 等語法形式,這樣一不當心就會形成這些內部方法的無窮遞歸調用。
可是UserDict就不會有此類問題。
UserDict有一個data的屬性,是dict的實例。用戶定義UserDict的子類時若是重寫方法,並不會遞歸調用UserDict的其餘方法,而是對UserDict.data
進行操做,這樣就減小了用戶自定義dict時防範死循環遞歸的難度。
eg:
import collections class Modified_Dict(collections.UserDict):#繼承自UserDict def __missing__(self,key): if isinstance(key, str):#防止遞歸循環,及時拋出keyerror raise KeyError(key) return self[str(key)] def __contains__(self,key): return str(key) in self.data def __setitem__(self, key, item): self.data[str(key)]=item dict4=Modified_Dict({'Ailee': 3, 'Bob': 5, 'Cindy': 5})#使用新dict類構造字典 print(dict4["Ailee"])#輸出:3 dict4.update({"one":1,"two":2}) print(dict4)#輸出:{'Ailee': 3, 'Bob': 5, 'Cindy': 5, 'one': 1, 'two': 2}
錯誤示範:這裏應該加圓括號創建自定義dict的空字典,不然以後的數據沒法被更新
dict5=Modified_Dict dict5.update({"one":1,"two":2}) print(dict5)#<class '__main__.Modified_Dict'>發現update失敗 -_-!
UserDict繼承自Mapping基類,諸如MutableMapping.update和Mapping.get也很實用。(截止2017.12.15 未掌握Mapping.get)
從Python3.3開始,type模塊引入了一個封裝類名叫作MappingProxyType。MappingProxyType提供一個可讀的動態映射視圖,即用戶沒法從這個視圖對原映射進行改動,可是原映射有改動時能夠經過這個視圖觀察到。
此類型特色在於防止用戶錯誤的修改映射。
from types import MappingProxyType Prize_number={'Ailee': 3, 'Bob': 5, 'Cindy': 5} dict6=MappingProxyType(Prize_number) dict6["Ailee"]=6#不支持改動。TypeError: 'mappingproxy' object does not support item assignment print(dict6) Prize_number["Ailee"]=6 print(dict6)#{'Ailee': 6, 'Bob': 5, 'Cindy': 5}原映射改動可視。
collections.OrderedDict
OrderedDict可以記住key的插入前後順序。
eg:
from _collections import OrderedDict d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2} print(OrderedDict(sorted(d.items()))) print(OrderedDict(sorted(d.items(),key=lambda t :t[1])))
輸出:
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)]) OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
在以前第二章namedtuple中也提到過。namedtuple的實例方法_asdict()把具名元組以collections.OrderedDict的形式返回。
collections.ChainMap
ChainMap能夠容納數個不一樣的映射對象,而後在進行鍵查找操做的時候,這些對象會被當成一個總體被逐個查找,直到鍵被找到爲止。
查詢規則片斷:
import builtins pylookup = ChainMap(locals(), globals(), vars(builtins))
想了解更多:
https://docs.python.org/3/lib...
collections.Countercounter用來統計目標集合中不一樣的元素及其頻數,利用most_common([n])返回前n個頻數最高的值以及相應的計數。
eg:
from collections import Counter ct=Counter('wasdddsasd') print(ct)#Counter({'d': 4, 's': 3, 'a': 2, 'w': 1}) ct.update("dassddd") print(ct.most_common(2))#[('d', 8), ('s', 5)]
定義:Python標準文庫給出的定義:A set object is an unordered collection of distinct hashable objects.
翻譯過來就是:set是一個包含不一樣可散列對象的無序集合
種類:集合這種數據結構包含set和frozenset,二者的區別在於後者不可變而前者可變,相似於元組之於列表。所以frozenset相比set不具有修改一類的方法。
本質:集合是許多惟一對象的彙集,因此能夠用來去重。
新建set:
新建frozenset:
集合推導:
集合推導在大括號中進行,思路與列表推導,字典推導相似。
eg:
set3={chr(i)for i in range(100,110)} print(set3)#{'k', 'f', 'i', 'e', 'd', 'm', 'l', 'g', 'j', 'h'}
set的操做方法包含frozenset的操做方法,區別在於frozenset不支持就地改變集合的方法,這一點與元組很相似。
下面展現set的操做方法,其中涉及修改自己的不適用於frozenset
若想深刻理解dict和set,首先須要瞭解它們背後的散列表。
散列(hashing)是電腦科學中一種對資料的處理方法,經過某種特定的函數/算法(稱爲散列函數/算法)將要檢索的項與用來檢索的索引(稱爲散列,或者散列值)關聯起來,生成一種便於搜索的數據結構(稱爲散列表)。也譯爲散列。舊譯哈希(誤覺得是人名而採用了音譯)。它也經常使用做一種資訊安全的實做方法,由一串資料中通過散列算法(Hashing algorithms)計算出來的資料指紋(data fingerprint),常常用來識別檔案與資料是否有被竄改,以保證檔案與資料確實是由原創者所提供。
衝突
。具備相同函數值的關鍵字對該散列函數來講稱作同義詞。綜上所述,根據散列函數f(k)和處理衝突的方法將一組關鍵字映射到一個有限的連續的地址集(區間)上,並以關鍵字在地址集中的「像」做爲記錄在表中的存儲位置,這種表便稱爲散列表,這一映射過程稱爲散列造表或散列,所得的存儲位置稱散列地址。減小衝突的方法:
散列表的存儲特色:
衡量散列表的利用率有一個概念叫作載荷因子:
`α= 已有的元素個數/表的長度`
載荷因子越大,插入到散列表中的元素越多,產生衝突的機率隨之增大。所以一般載荷因子被設計成0.75,保證必定的表元是空的。散列表的存儲特色決定了它耗費存儲空間的特色。
散列表本質要解決的是查找時間的問題。若是順序查找的話,時間複雜度爲O(n);而散列表,時間複雜度則爲O(1)!直接甩了一個次元,這也就是爲何在大量數據存儲查找的時候,散列表獲得大量應用的緣由。
注:散列表知識引自
做者:SakuraWood
連接:https://juejin.im/post/5a1bd0...
來源:掘金
給定一個鍵,要麼返回查詢值,要麼拋出keyerror。
Tips:不要對字典同時進行修改和迭代。
由於你的修改有可能致使鍵的次序發生變化,從而在迭代中遺漏某些數據