python程序設計 課件 第5、6章 字典與集合、序列與迭代_第1頁
python程序設計 課件 第5、6章 字典與集合、序列與迭代_第2頁
python程序設計 課件 第5、6章 字典與集合、序列與迭代_第3頁
python程序設計 課件 第5、6章 字典與集合、序列與迭代_第4頁
python程序設計 課件 第5、6章 字典與集合、序列與迭代_第5頁
已閱讀5頁,還剩167頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領

文檔簡介

第5章字典與集合Python程序設計第5章字典與集合Python提供了豐富的序列類型、內(nèi)置函數(shù)、方法以及語法支持等,讓我們能夠輕松運用序列形式的容器(數(shù)據(jù)結(jié)構(gòu))對象。但數(shù)據(jù)并非只有順序的形式,也有“映射”形式的數(shù)據(jù)結(jié)構(gòu),即“字典”,其內(nèi)含元素都是所謂的“鍵值配對”,從鍵映射到值。第5章字典與集合此外,集合也是常見的數(shù)據(jù)類型,具有嚴謹?shù)臄?shù)學定義,可用來存放東西(對象),但只能放進去一次,也就是記錄某東西“在不在”里面。集合既非序列、也非映射,自有其一套機制,且與字典相關(guān)。有了列表與字典,幾乎就可組合出絕大部分所需的數(shù)據(jù)結(jié)構(gòu)。當談到某種數(shù)據(jù)類型時,不外乎學習如何建立、存與取(寫與讀)、搜尋、排序、分類、過濾等等操作動作。關(guān)于雜湊1字典與集合的訪問接口4字典結(jié)構(gòu)2集合類型3目錄5.1

關(guān)于雜湊5.1

關(guān)于雜湊雜湊(Hashing)是計算機科學中一種對數(shù)據(jù)的處理方法,通過某種特定的函數(shù)/算法(稱為雜湊函數(shù)/算法),將要檢索的項與用來檢索的索引(稱為雜湊,或者雜湊值)關(guān)聯(lián)起來,生成一種便于搜索的數(shù)據(jù)結(jié)構(gòu)(稱為雜湊表,散列)。它也常被用作一種信息安全的實施方法,由一串數(shù)據(jù)中經(jīng)過雜湊算法計算出來的數(shù)據(jù)指紋,經(jīng)常用來識別文檔與數(shù)據(jù)是否有被篡改。5.1

關(guān)于雜湊常見的雜湊演算法有MD5、SHA-1、SHA-256等等,可用來求得數(shù)據(jù)的數(shù)字指紋,其應用相當廣泛,例如網(wǎng)站若提供大型文件,可一并附上該文件的雜湊值,下載后便能以雜湊值檢查文件是否完整無缺。P2P(peer-to-peer)對等網(wǎng)絡文件分享,諸如BitTorrent、eMule、BitComet,也會使用雜湊技術(shù),借以檢驗下載的文件片段是否正確。雜湊因具有不可逆的性質(zhì),無法從雜湊值逆向推算原先的數(shù)據(jù),所以也用于密碼加密,當要存儲密碼時,并非直接存儲,而是存儲密碼的雜湊值,即便被偷了,也無法破解得知密碼。5.1

關(guān)于雜湊字典(dict)類型含有鍵值配對,只要是可雜湊的對象(不可變對象都可雜湊)都能作為鍵,所以不像列表受限于從0起跳的連續(xù)整數(shù)索引值,字典可使用不連續(xù)的大數(shù)字作為鍵,例如學生的學號,也可使用字符串作為鍵,例如汽車的車牌、日期時間、人物姓名、水果名等等;集合類型則是儲存某些東西的數(shù)據(jù)結(jié)構(gòu),同樣的,可雜湊的對象方能成為集合的元素。字典類型在其他程序語言里可能稱為關(guān)聯(lián)式陣列、雜湊表、映射等,概念相同但細節(jié)不同。字典的創(chuàng)建字典的鍵與值字典生成式全局與局部字典應用實例5.2字典結(jié)構(gòu)5.2

字典結(jié)構(gòu)與列表(list)是存儲一個個的對象,并以索引值(從0起跳的整數(shù))存取不同,字典是存儲鍵值配對,透過鍵來存取相對應的值。建立字典時可使用大括號“{}”,里面放進以逗號隔開的鍵值配對,鍵與值則以冒號“:”隔開,存取時仍使用方括號“[]”,在方括號內(nèi)放入鍵,便可存取相關(guān)聯(lián)的值。>>>d={} #建立空字典對象>>>d={'name':'Amy','age':27} #以大括號語法建立dict對象>>>d #字典不具順序性{'age':27,'name':'Amy'}>>>d['name'] #以方括號存取,類似于list'Amy'>>>d['age']=29 #dict是可變對象,可指派新值>>>d['score']=77 #但若是首次指派不存在的鍵,>>>d #便會建立該鍵值配對的關(guān)系{'age':29,'name':'Amy','score':77}>>>d['ape'] #但若取用時字典無此鍵,會發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:'ape' #鍵錯誤5.2

字典結(jié)構(gòu)5.2

字典結(jié)構(gòu)圖5-1示意了上述程序代碼里的字典對象d,看起來跟列表很像,但字典的鍵不限定于整數(shù)。

字典示意圖5.2.1字典的創(chuàng)建字典是一種映射形式的類型,從“鍵”映射到“值”,值可以是任何類型的對象,例如字符串、整數(shù)、列表或另一個字典,鍵則是不可變對象,例如字符串、整數(shù)、元組,最常使用的是字符串與整數(shù)。使用字符串的話,便能以容易記憶的字符串作為鍵,若鍵為整數(shù)的話,可使用任何范圍的整數(shù),不像表的索引值必須從0開始計數(shù)。>>>d={'str':[0,1,2],(30,41,52):{'x':0,'y':1}}>>>d[(30,41,52)] #此鍵是個tuple,{'x':0,'y':1} #其值是個dict>>>d={1504301:'Amy',1504305:'Bob'}>>>d[1504301] #以學號(很大的整數(shù))作為鍵'Amy'5.2.1字典的創(chuàng)建除了以大括號包起來的字面值形式,也能透過內(nèi)置函數(shù)dict(也是類型dict的構(gòu)造函數(shù))來建立字典對象,其參數(shù)的形式非常多,非常方便。>>>d=dict(name='Amy',age=40,job='writer’)#以關(guān)鍵字參數(shù)指定鍵值配對>>>d #你看到的順序,不一定會和此處相同{'age':40,'job':'writer','name':'Amy'}>>>keys=['name','age','job'] #含有鍵的列表對象>>>values=['Amy',40,'writer']#含有值的列表對象>>>d2=dict(zip(keys,values)) #透過內(nèi)置函數(shù)zip組合>>>d2 #雖內(nèi)容物與d相同,但順序不同{'job':'writer','age':40,'name':'Amy'}5.2.1字典的創(chuàng)建>>>d3=dict(d2,name='Amanda')#以d2為基礎,并以關(guān)鍵字參數(shù)>>>d3#指定鍵值配對{'job':'writer','age':40,'name':'Amanda'}>>>d4=dict(zip(range(100,100+3),('Amy','Bob','Cathy')))>>>d4{100:'Amy',101:'Bob',102:'Cathy'}5.2.1字典的創(chuàng)建5.2.1字典的創(chuàng)建字典建立后,接下來便是各種支持字典的運算符與方法。>>>d={'age':29,'name':'Amy','score':77}>>>len(d) #長度,含有幾個鍵值配對3>>>'age'ind #測試字典有無此鍵True>>>deld['name'] #刪除此鍵(以及相對應的鍵值配對關(guān)系)>>>'name'indFalse>>>d['name'] #以[]形式取用,若無該鍵會發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:'name'>>>d.get('name')#以方法get取用,若無該鍵會返回None>>>d.get('name','Amy')#若無該鍵就返回指定的缺省值,'Amy'#注意,字典內(nèi)仍無該鍵>>>d['name']='Amy'#以[]形式指派鍵值配對>>>d.setdefault('job')#以方法setdefault指派,若無該鍵,>>>d #缺省會指派為None{'job':None,'age':29,'score':77,'name':'Amy'}5.2.1字典的創(chuàng)建>>>d.setdefault('city','London') #加入此鍵值配對>>>d.setdefault('city') #已有此鍵,返回相對應的值'London'5.2.1字典的創(chuàng)建5.2.1字典的創(chuàng)建雖然字典是一種映射形式的類型,但提供了各種方法,能把鍵、值,甚至是鍵值配對以符合序列類型的對象返回,那么,我們就能用“迭代”方法逐一處理。d={'a':1,'b':2,'c':3} #類型dictforkeyind: #放在for/in循環(huán)里,會提供鍵的可迭代項

print(key)forkeyind.keys(): #方法keys(),提供鍵的可迭代項

print(key)forvalueind.values(): #方法values(),提供值的可迭代項

print(value)fork,vind.items(): #方法items(),提供鍵值配對的可迭代項

print(k,v)5.2.1字典的創(chuàng)建再強調(diào)一次,字典類型的鍵值配對并無順序,就算是同樣的程序代碼,如果放到不同版本的Python來執(zhí)行、放到另一臺電腦上執(zhí)行,其結(jié)果所顯示的順序不一定相同,甚至每次執(zhí)行的結(jié)果都不同;換句話說,字典只提供鍵映射到值這樣的關(guān)系而已,并不保證順序性。字典類型的操作列于表7-1。5.2.1字典的創(chuàng)建表5-1字典操作5.2.2字典的鍵與值只要符合抽象類型Hashable(可散列)的對象,就能作為字典的鍵,最常用的是類型str(字符串)與int(整數(shù));可變類型如list、set、dict,都無法作為鍵。注意到整數(shù)1與浮點數(shù)1.0的雜湊值相同,但因為類型?oat無法精確地表示大部分的浮點數(shù),并不適合拿來作為字典的鍵,實際中也很少有人使用。>>>fromdecimalimportDecimal>>>fromfractionsimportFraction>>>d={1:111,'a':2,Decimal('3.14'):'pi',Fraction(1,3):1.3}>>>d{'a':2,1:111,Decimal('3.14'):'pi',Fraction(1,3):1.3}>>>hash(1.0),hash(1)(1,1) #雜湊值相同,>>>d[1.0],d[1] #所以會得到相同的對應值(111,111)5.2.2字典的鍵與值5.2.2字典的鍵與值若鍵是字符串,也能使用構(gòu)造函數(shù)dict來建立字典對象,但因為需要以參數(shù)的形式傳入,所以該鍵(字符串)必須符合Python的識別字規(guī)則,例如字符串'123abc'就無法以此形式建立。>>>dict(foo=1,bar=2,abc123=3)#以關(guān)鍵字參數(shù)傳入鍵與值{'foo':1,'bar':2,'abc123':3}>>>dict(foo=1,bar=2,123abc=3)#123abc不符合識別字規(guī)則

File"<stdin>",line1dict(foo=1,bar=2,123abc=3)^SyntaxError:invalidsyntax>>>d={'foo':1,'bar':2,'123abc':3} #以這種形式就沒問題了{'foo':1,'bar':2,'123abc':3}>>>d2=dict(d,foo=555,abc=999) #以先前的字典d為基礎,>>>d2#再以關(guān)鍵字參數(shù)新增或更新鍵值配對{'foo':555,'abc':999,'bar':2,'123abc':3}>>>k=['foo','bar'];v=[101,102] #以zip合并兩個列表,>>>d3=dict(zip(k,v),abc=999) #便可作為鍵值配對的提供者>>>d3{'foo':101,'abc':999,'bar':102}5.2.2字典的鍵與值5.2.2字典的鍵與值另外也能以字典類型的方法fromkeys來建立列表,此方法不屬于某一個實體對象,所以稱為類型方法。其首個參數(shù)可以是序列對象(或可迭代項),提供鍵,后一個參數(shù)(選用性)則是缺省值,若不指定則會是None。>>>dict.fromkeys(['a','b','c']){'a':None,'b':None,'c':None}>>>dict.fromkeys(['a','b','c'],999){'a':999,'b':999,'c':999}>>>dict.fromkeys(range(5),0){0:0,1:0,2:0,3:0,4:0}5.2.3字典生成式用來建立字典對象的字典生成式與列表生成式非常類似,只是語法稍有不同,它使用大括號“{}”包起來,在for的左邊必須是“k:v”的形式,提供鍵值配對;同樣地,也能加上if進行過濾。{表達式:表達式for名稱in可迭代項}{表達式:表達式for名稱in可迭代項if表達式}>>>{k:k**2forkinrange(5)} #鍵從0到4,值是平方{0:0,1:1,2:4,3:9,4:16}>>>dict([(k,k**2)forkinrange(5)]) #使用列表生成式與構(gòu)造函數(shù)dict,{0:0,1:1,2:4,3:9,4:16} #達到同樣的效果>>>d={'Amy':90,'Joe':45,'Kevin':33}>>>{k:vfork,vind.items()ifv<60}#僅拿出成績低于60的{'Joe':45,'Kevin':33}>>>li=['a','b','c']>>>{a:ifori,ainenumerate(li,start=101)}{'a':101,'b':102,'c':103} #為每個元素賦予某列舉值>>>dict(enumerate(li,start=1)) #呃,不一樣...{1:'a',2:'b',3:'c'}>>>dict(zip(li,range(101,101+len(li))))#弄出相同的字典了{'a':101,'b':102,'c':103}5.2.3字典生成式5.2.3字典生成式雖然某些字典生成式能以列表生成式與構(gòu)造函數(shù)dict改寫,但后者必須先建立列表對象,然后再傳入構(gòu)造函數(shù)dict,效能較為緩慢,從語法來看也較不簡潔。5.2.4全局與局部Python會把“名稱”放在命名空間里,也就是把名稱與對象的綁定關(guān)系存儲在字典里?!境绦?qū)嵗?-1】全局與局部命名空間。#symbol_table.pya=1b=2c=3deffoo(x,y):a='aaa'b='bbb'print(locals())print(globals())print('*'*10)foo(80,91)5.2.4全局與局部5.2.4全局與局部名稱a、b、c、foo被放在全局命名空間里。若調(diào)用函數(shù)foo,會進入函數(shù)局部范圍,名稱x、y、a、b會被放在局部的命名空間里。以Python內(nèi)置函數(shù)globals取得含有全局名稱(與對象綁定關(guān)系)的字典,也稱為全局符號表,以locals取得含有局部名稱的字典(局部符號表)。上述程序代碼執(zhí)行后,會輸出如下的信息:{'a':1,'c':3,'b':2,'__builtins__':<module'builtins'(built-in)>,'__file__':'symbol_table.py','__package__':None,'__cached__':None,'__name__':'__main__','foo':<functionfooat0x7fe2d8ec>,'__doc__':None}**********{'a':'aaa','y':91,'b':'bbb','x':80}5.2.4全局與局部在全局字典里,你可看到名稱a、b、c、foo以及一些其他名稱,例如__?le__指向文件名的字符串,__name__則是目前所在模塊名,而__builtins__則指向內(nèi)置模塊,也因為有它,Python才知道要去哪尋找內(nèi)置的函數(shù)、常數(shù)、異常。在局部字典里,可看到x、y、a、b,因字典不具順序性,所以并不會按照參數(shù)的順序、賦值語句的先后順序排列名稱。5.2.5字典應用實例若需要根據(jù)某對象的內(nèi)容拿出相對應的數(shù)據(jù)或執(zhí)行不同的程序代碼,往往會使用if/elif/elif.../else來實作,但可能太過冗長;可以把東西先放進字典,然后直接取用即可。x='apple'result=Noneifx=='apple': #以一連串的if/elif.../else來做判斷,

result=1elifx=='banana':result=2elifx=='grape': #該執(zhí)行哪部分的程序代碼

result=3else:result=-1####上述程序代碼可改寫為:####x='apple'd={'apple':1,'banana':2,'grape':3}result=d.get(x,-1)5.2.5字典應用實例5.2.5字典應用實例因為函數(shù)也是對象,也可作為值放進字典里,這樣一來便可根據(jù)鍵來執(zhí)行相對應的程序碼,例如:deff0():pass #準備一些函數(shù)deff1():passdeff2():pass #作為值放進字典里d={'apple':f0,'banana':f1,'grape':f2}x='apple'ifxind: #若有該鍵,

d[x]() #便取出對應的值(函數(shù)對象)并調(diào)用執(zhí)行若字典的值可雜湊,便可逆轉(zhuǎn)鍵值配對。不過若值重復的話,逆轉(zhuǎn)后將會只剩一個鍵值配對?!境绦?qū)嵗?-2】逆轉(zhuǎn)字典的鍵值配對。#dict_invert.py#學生姓名與成績d={'Amy':45,'Bob':50,'Cathy':62,'David':45,'Eason':63,'Fred':78,'George':72,'Helen':82,'Ivan':100,'Jason':98,'Kevin':0,'Laura':100}#逆轉(zhuǎn)鍵值配對d2={v:kfork,vind.items()}#逆轉(zhuǎn)后,鍵100、值可能會是'Laura'或'Ivan',只剩一個5.2.5字典應用實例5.2.5字典應用實例因為字典不具順序性,若想要按照某種排序方式輸出或操作字典的話,可運用內(nèi)置函數(shù)sorted來排序。可再傳入一個名為key的參數(shù),由它來指定根據(jù)什么來排序?!境绦?qū)嵗?-3】排序字典。#dict_sort.py#鍵是學生姓名,值是一個含有三項成績的tuple對象,假設成績依序是數(shù)學、歷史、英文d={'Amy':(45,60,33),'Bob':(50,62,78),'Cathy':(62,98,87),'David':(45,22,12),'Eason':(63,55,71),'Fred':(78,79,32)}forkinsorted(d.keys()): #根據(jù)鍵(姓名)排序

print(k,d[k])5.2.5字典應用實例deffoo(item):returnitem[1][2] #根據(jù)英文成績來排序foriteminsorted(d.items(),key=foo):print(item)defbar(item):returnsum(item[1]) #根據(jù)總分來排序foriteminsorted(d.items(),key=bar):print(item)5.2.5字典應用實例5.2.5字典應用實例字符串格式化,包括運算符“%”與字符串方法format,都可以運用字典。集合的創(chuàng)建集合的元素集合的數(shù)學運算集成生成式5.3集合類型5.3

集合類型接下來,我們來看看類型set(集合)。集合是一種不具順序性的容器類型,同一個對象只能放進去一次。建立集合時,字面值語法是以大括號“{}”包住想要的一個個元素,或調(diào)用內(nèi)置函數(shù)set,傳入可迭代項(提供一個個元素)。元素可以是任何不可變對象,例如int、tuple、str。>>>x={1,2,3,4,5} #以大括號包住集合元素>>>x{1,2,3,4,5}>>>x2=set([1,'a',(3,4),5]) #內(nèi)置函數(shù)set,傳入含有元素的列表>>>x2{1,'a',(3,4),5}>>>x3=set(range(1,5+1)) #內(nèi)置函數(shù)set,傳入含有元素的可迭代項>>>x4={0,1,[2,3,4]} #list是可變對象,發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unhashabletype:'list'5.3

集合類型5.3

集合類型上述程序中,x2對象的示意如圖所示。與字典相同,集合內(nèi)的元素也不具有順序性,類型set僅代表某東西是否存在于該集合內(nèi)。

集合示意>>>x={};type(x) #{}會建立空字典,不是空集合<class'dict'>>>>x={1,2,3,4,5}>>>x[0] #集合不具順序性,不支持索引存取方式Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:'set'objectdoesnotsupportindexing>>>len(x) #長度,集合內(nèi)元素的個數(shù)5>>>1inx #判斷某對象存不存在于集合內(nèi)True5.3

集合類型>>>x.add(6) #加入新元素>>>x.add(6) #加入已有的元素,無作用>>>x.remove(1) #移除某元素>>>x.remove(7) #若該元素不存在于集合內(nèi),發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:7>>>x.discard(7) #移除某元素,若不存在并不會發(fā)生錯誤>>>x{2,3,4,5,6}5.3

集合類型>>>x&{5,6,999} #“&”運算符,交集(intersection){5,6}>>>x|{5,6,999} #“|”運算符,聯(lián)集(union){2,3,4,5,6,999}>>>x-{5,6,999} #“-”運算符,差集(difference){2,3,4}>>>x^{5,6,999} #“^”運算符,對稱差集(symmetricdifference){2,3,4,999}5.3

集合類型5.3

集合類型當想檢查兩個列表是否含有相同的元素,但不在乎順序與是否重復時,便可利用類型set,非常便利。>>>li0=[0,1,2];li1=[2,2,1,0,2,1]#不同的列表>>>set(li1) #傳入內(nèi)置函數(shù)set,得到不重復元素的集合{0,1,2}>>>set(li0)==set(li1) #以集合的眼光來看是相等的True5.3

集合類型集合類型是一種不具順序性的容器類型,其元素必須是可雜湊的對象,記錄某對象在不在集合里。因為不具順序性,所以集合不支持索引與切片的存取方式。5.3.1集合的創(chuàng)建建立集合時,可使用被圓括號“()”包住、以逗號“,”隔開元素的字面值形式,或是使用類型set的構(gòu)造函數(shù)。調(diào)用構(gòu)造函數(shù)set時,參數(shù)是個可迭代項,負責提供一個個的元素,元素必須可雜湊。>>>empty_dict={} #這是空字典>>>empty_set=set() #這才是空集合>>>{'a','b','c'} #含有三個元素的集合{'c','b','a'} #集合不具順序性>>>{(0,1,2),3,3.14} #元素可以是任何不可變對象{(0,1,2),3.14,3}>>>set('abc') #使用構(gòu)造函數(shù)set,傳入序列對象{'c','b','a'}>>>set([30,41,52]) #傳入列表{41,52,30}5.3.1集合的創(chuàng)建>>>set(range(5)) #傳入可迭代項{0,1,2,3,4}>>>s0=set(range(5))>>>set(s0) #傳入另一個集合對象{0,1,2,3,4}>>>len(s0) #長度5>>>1ins0 #判斷里面是否含有某元素True5.3.1集合的創(chuàng)建5.3.2集合的元素集合建立后,因為是可變對象,可使用集合專屬的方法原地修改其元素。>>>s={0,1,2,3,4,5}>>>s.add(6) #加入int對象6>>>s.add(3) #加入已有的元素,無作用>>>s.remove(0) #移除0>>>s{1,2,3,4,5,6}>>>s.remove(999) #若想移除的元素不存在,引發(fā)異常Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:9995.3.2集合的元素>>>s.discard(999) #使用discard的話,元素不存在不會引發(fā)異常>>>s.pop() #隨機移除某元素、返回該元素2>>>s{3,4,5,6}>>>s1=s.copy() #淺復制>>>s.clear() #清空集合>>>s.pop() #若集合為空,pop會引發(fā)異常Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:'popfromanemptyset'5.3.2集合的元素5.3.3集合的數(shù)學運算集合有嚴謹?shù)臄?shù)學定義,Python也以運算符和方法支持集合的數(shù)學運算,包括聯(lián)集、交集、差集、對稱差集以及子集合、超集合的判斷。>>>s0={1,2,3,4} #測試用集合>>>s1={3,4,5,6,7}>>>s2={1,2,3}>>>s3={91,92,93}>>>s0==s1,s0==s0 #判斷是否含有同樣的元素(False,True)>>>s2<=s0,s2.issubset(s0) #子集合(True,True)>>>s2<s0,s0<s0 #真子集合(True,False)5.3.3集合的數(shù)學運算>>>s0>=s2,s0.issuperset(s2) #超集合(True,True)>>>s0>s2,s0>s0 #真超集合(True,False)>>>s0|s1 #聯(lián)集,產(chǎn)生新集合對象{1,2,3,4,5,6,7}>>>s0&s1 #交集,產(chǎn)生新集合對象{3,4}>>>s0-s1 #差集,產(chǎn)生新集合對象{1,2}5.3.3集合的數(shù)學運算>>>s0^s1 #對稱差集,產(chǎn)生新集合對象{1,2,5,6,7}>>>s0&s3,s0.isdisjoint(s3) #無交集(set(),True)5.3.3集合的數(shù)學運算5.3.3集合的數(shù)學運算同一種集合運算,例如聯(lián)集,有運算符“|”與方法“union”可用,但運算符只能接受集合對象,而方法則可接受可迭代項,例如列表。>>>s0={1,2,3,4}>>>s0|[3,4,5,6] #運算符只能接受集合對象Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unsupportedoperandtype(s)for|:'set'and'list'>>>s0.union([3,4,5,6]) #方法可以接受可迭代項{1,2,3,4,5,6}>>>set('abc')&'bcd'Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unsupportedoperandtype(s)for&:'set'and'str'>>>set('abc').intersection('bcd'){'c','b'}5.3.3集合的數(shù)學運算5.3.3集合的數(shù)學運算使用增強型賦值語句“|=、&=、-=、^=”時須小心,這是原地修改的動作,不會產(chǎn)生新集合對象,而是更新(修改)語句左邊的集合對象。>>>s0={1,2,3,4}>>>s0|={3,4,5,6} #只示范“|=”,其余都類似>>>s0{1,2,3,4,5,6}5.3.4集合生成式集合也有集合生成式可用,其語法和列表生成式、字典生成式類似,一般形式如下:{表達式for名稱in可迭代項}{表達式for名稱in可迭代項if表達式}>>>li=['a','bar','candy','o','car']>>>{len(x)forxinli}{1,3,5} #記錄曾出現(xiàn)過的字符串長度>>>{xforxinliiflen(x)==1}{'a','o'} #只收錄長度為1的字符串>>>li=[0,1,1,2,2,3,3,4,5,5,6]>>>{xforxinliifli.count(x)==2}{1,2,3,5} #挑出在原列表出現(xiàn)2次的元素5.4字典與集合的訪問接口可雜湊項映射5.4

字典與集合的訪問接口在學習了字典與集合的基本知識后,我們來介紹相關(guān)的抽象類型,了解字典與集合提供哪些存取接口。5.4.1可雜湊項之前介紹類型dict時,我們說“鍵”可以是任何不可變對象,這種說法還不夠正確,應該說任何“可雜湊項(hashable)”都可作為字典的鍵。Python有個抽象類型Hashable,符合此接口的對象可依照雜湊演算法算出獨一無二的雜湊值,只要對象不相同,就會得到不同的雜湊值。Python內(nèi)置的不可變類型都符合此抽象類型,而內(nèi)置的可變類型則都不符合。調(diào)用內(nèi)置函數(shù)hash可得到某對象的雜湊值,但實際中很少直接使用該值。>>>fromcollections.abcimport*>>>issubclass(int,Hashable) #內(nèi)置的不可變類型都符合Hashable接口True>>>issubclass(tuple,Hashable)True>>>issubclass(list,Hashable) #內(nèi)置的可變類型,不符合Hashable接口False #因為可變對象的內(nèi)容會改變,其雜湊值不定>>>issubclass(dict,Hashable)False>>>hash(1),hash('abc'),hash((30,41,52))(1,-39521455,1696699620) #不可變對象擁有獨一無二的雜湊值5.4.1可雜湊項>>>a=1;b='abc';c=(30,41,52)>>>hash(a),hash(b),hash(c) #既然a==1為真,內(nèi)容相同,(1,-39521455,1696699620) #那么其雜湊值也會相同>>>hash([0,1,2]) #不可變對象沒有雜湊值Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unhashabletype:'list'>>>hash('name'),hash('age'),hash('score')(1574142124,537780116,-23663026)>>>hash(1),hash('a'),hash((3,4)),hash(5)(1,738615097,1699342716,5)5.4.1可雜湊項5.4.1可雜湊項不可變對象在其生存周期內(nèi)內(nèi)容絕對不變,所以其雜湊值不僅獨一無二,而且也不會改變,可作為字典的存儲依據(jù);所以較正確的字典示意圖應如圖所示,字典其實是存儲“對象的雜湊值”與“值(任何對象)”的配對關(guān)系。下次當你給定某鍵時,字典就會算出該鍵的雜湊值,然后取出相對應的值。

字典示意圖5.4.1可雜湊項若對象屬于容器類型,那么不僅對象本身必須不可變,其內(nèi)容也必須不可變,方可算是不可變對象。所以,tuple對象本身雖不可變,但若含有可變對象(例如list),那么該對象就不算是不可變,無法作為字典的鍵。>>>li=[0,1,2] #list對象,可變>>>t=(3,4,li) #tuple對象,含有可變對象>>>d={'a':1,t:2} #無法作為字典的鍵Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unhashabletype:'list'5.4.1可雜湊項同樣的道理,類型set也不是直接存儲某對象,而是存儲雜湊值。當把某對象放進集合時,會先算出雜湊值再放進去;當要測試某對象存不存在集合內(nèi)時,只要算出對象的雜湊值,然后再檢查即可(見圖示意)。

集合示意圖5.4.1可雜湊項因為雜湊演算法的特性,給定某內(nèi)容(對象)就算出相對應的雜湊值,若對象不同就必定會得到不一樣的雜湊值,如此一來便能輕松得到字典與集合的功能。還請注意,不同Python版本可能會使用不同的雜湊演算法,不同平臺的Python實現(xiàn)也可能使用不一樣的雜湊演算法,所以你執(zhí)行上述程序時,不會得到跟實例一樣的雜湊值。5.4.2映射項圖7-5顯示了與以映射(Mapping)為中心的相關(guān)抽象類型,類型dict符合抽象類型MutableMapping(可變映射)定義的接口,而且dict也是目前Python唯一的內(nèi)置映射類型;類型set符合抽象類型Set定義的接口。

映射相關(guān)的抽象類型層級5.4.2映射項我們先從類型set的抽象類型Set著手,因為它是個容器類型,很自然地繼承了抽象類型Sized與Container,所以可使用內(nèi)置函數(shù)len算出長度(含有元素個數(shù)),并以運算符“in”檢查某對象是否存在于集合之中。而且也符合可迭代項接口,所以可使用內(nèi)置函數(shù)iter得到迭代器,然后調(diào)用next逐一取出其中的元素,只不過因為類型set不具順序性,所以我們并不知道迭代器會以何順序給出元素。>>>x={2,'a',(3,5),'b',17} #類型set>>>len(x) #長度,元素的個數(shù)5>>>'b'inx #成員關(guān)系運算符True>>>xit=iter(x)#取得迭代器>>>next(xit),next(xit)#取出前兩個元素,(17,'a')#注意,不具順序性>>>[eforeinxiftype(e)isint]#既然符合Iterable接口[17,2] #所以可用于需要迭代協(xié)定的地方5.4.2映射項5.4.2映射項然后是類型dict符合的接口面,抽象類型Mapping(映射)與MutableMapping(可變映射),同樣的,因為屬于容器類型,所以也都繼承了Sized與Container,并擁有許多字典專屬的方法。>>>d={'name':'Amy','age':40,'score':77}>>>len(d) #長度,鍵值配對的個數(shù)3>>>'age'ind #“in”運算符,某鍵是否存在True>>>d['name'] #以鍵取出值'Amy'>>>d['job']='wrier' #重新指派,新增或更新>>>d.pop('name') #移除指定鍵(與其鍵值配對)'Amy'5.4.2映射項>>>d.pop('abc') #若不存在該鍵,發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:'abc'>>>d.pop('abc','default')#指定不存在該鍵時應返回的缺省值'default'>>>d.update({'name':'John','age':33})#傳入可提供鍵值配對的對象,>>>d#更新原字典的內(nèi)容{'job':'wrier','age':33,'name':'John','score':77}>>>d2=d.copy() #淺復制>>>d.clear() #刪除字典的全部內(nèi)容5.4.2映射項5.4.2映射項類型dict符合的接口抽象類型Mapping,也繼承了Iterable,所以也能如同類型list與set,透過內(nèi)置函數(shù)iter拿到能迭代內(nèi)容的迭代器,再以next逐一取出。但因為字典含有鍵與值,較為復雜,所以又再提供能動態(tài)瀏覽字典內(nèi)容的抽象類型MappingView(映射瀏覽),從圖7-5你可得知,當要迭代字典的鍵與鍵值配對時,其接口(KeysView與ItemsView)是個符合Set與MappingView的可迭代項,因為鍵與鍵值配對不具順序性,所以繼承自抽象類型Set,而MappingView則提供動態(tài)瀏覽字典內(nèi)容的功能,當字典內(nèi)容改變時,符合MappingView接口的對象也能隨之反映;至于字典的值,因為有可能重復(不同鍵可映射到同樣的值),所以其接口(ValuesView)較為特殊,并不繼承自Set,除了繼承自MappingView動態(tài)反映字典內(nèi)容,另行繼承自Container與Iterable。5.4.2映射項換句話說,當想要以迭代方式操作字典時,作法是調(diào)用相對應的方法,得到能迭代鍵、值、或鍵值配對的可迭代項,如此即可。而因為這些可迭代項符合MappingView(映射瀏覽)接口,提供動態(tài)瀏覽字典內(nèi)容的特色,一旦字典內(nèi)容改變,也會隨之反映。>>>d={'a':1,'b':2,'c':3}>>>keys=d.keys() #以方法keys與values,取得>>>values=d.values() #鍵與值的映射瀏覽對象,>>>list(zip(keys,values)) #其順序互相對應[('a',1),('c',3),('b',2)]>>>deld['a'] #動態(tài)刪除某鍵(值配對)>>>list(keys) #也隨之反映['c','b']>>>keys&{'a','b'} #因為也是個集合,可做集合的運算{'b'}5.4.2映射項5.4.2映射項不過,若在迭代過程中對字典進行修改動作,例如刪除、新增、更新,都可能影響字典內(nèi)鍵值配對的順序,連帶影響迭代的結(jié)果,甚至可能出錯;反之,只要不作修改,方法keys()、values()、items()返回的映射瀏覽對象就能一致反映出正確的字典內(nèi)容與順序。>>>d={'a':1,'b':2,'c':3}>>>list(d.keys()) #雖然我們不知道字典內(nèi)鍵值配對的存放順序,['a','c','b']>>>list(d.values()) #但keys()、values()、items()返回的映射瀏覽對象,[1,3,2] #仍會呈現(xiàn)出一致的順序,>>>list(d.items()) #只要不去修改原字典的話[('a',1),('c',3),('b',2)]>>>dict(zip(d.keys(),d.values())) #因此,這一行程序代碼才能產(chǎn)生出{'a':1,'c':3,'b':2} #與原字典相同內(nèi)容的新字典對象5.4.2映射項>>>pairs=zip(d.values(),d.keys()) #這么做可顛倒鍵與值>>>list(pairs)[(1,'a'),(3,'c'),(2,'b')]>>>di=dict(zip(d.values(),d.keys()))#若字典的值都不相同且有雜湊值>>>di#便可得到顛倒鍵值關(guān)系的字典{1:'a',2:'b',3:'c'}5.4.2映射項5.4.2映射項至此,讀者應該了解字典與集合的基本用法與概念,明白與列表有何不同,并且知道相關(guān)的抽象類型與接口。謝謝大家第6章序列與迭代Python程序設計第6章序列與迭代Python的序列類型包括列表、元組、字符串、文件等,存取這些抽象類型時,核心的概念就是迭代,一個接著一個地逐一操作。這種形式的數(shù)據(jù)不勝枚舉,所以Python提供了充分的支持。以列表為中心,圍繞著許多相關(guān)類型、內(nèi)置函數(shù)、方法、列表生成式等,以方便我們在程序里處理序列形式的數(shù)據(jù)。事實上,順序、條件判斷、再加上迭代(重復)的執(zhí)行形式,就組成了基本的程序流程。類型與對象1迭代的概念4抽象數(shù)據(jù)類型2元素的訪問3列表生成式5目錄6.1類型與對象類型即對象命名空間6.1

類型與對象我們已經(jīng)熟悉類型、對象、名稱(標識符)之間的關(guān)系了,也學會了不少類型,包括整數(shù)(int)、浮點數(shù)(?oat)、列表(list)、字符串(str)等等,甚至函數(shù)(function)、模塊(module)也都是類型。例如:>>>a=22;b=4+5j;c='hi';d=(169,44)>>>e=['Amy',a,d]>>>defsq(x):returnx*x...>>>importmath這里的a、b、c、d、e、sq、math都是名稱,分別指向某種對象,而對象必定屬于某種類型,規(guī)定了該對象的接口與用法;我們可用內(nèi)置函數(shù)type查出某對象的類型,type()會顯示<class'XXX'>。>>>type(a);type(c);type(e) #查詢對象的類型<class'int'><class'str'><class'list'>>>>type(sq);type(math) #模塊與函數(shù)也是對象,<class'function'>#也有類型<class'module'>6.1

類型與對象6.1.1類型即對象所謂“類型(type)”在程序里也是以對象形式存在的,使用type()得到代表類型的對象后,可以比較判斷兩個對象的類型是否相同;有些基本類型直接輸入其名稱便可得到代表該類型的對象,有些類型則被放在某個模塊內(nèi)。>>>it=type(a) #類型是對象,也能命名>>>it<class'int'>>>>type(a)istype(99) #比較類型是否為同一個True>>>int,float,complex #基本的類型,直接取用(<type'int'>,<type'float'>,<type'complex’>)>>>tuple,list,str(<class'tuple'>,<class'list'>,<class'str'>)>>>type(e)islist #直接取用類型作判斷True>>>importtypes #有些類型被放在模塊內(nèi)>>>type(sq)istypes.FunctionTypeTrue>>>type(math)istypes.ModuleTypeTrue6.1.1類型即對象6.1.1類型即對象整數(shù)對象的類型是int,類型既然是對象,那么它也有類型,類型對象的類型是type。>>>a=3 #名稱a指向int對象,值為3>>>type(a) #其類型是int<class'int'>>>>at=type(a)#名稱at指向type對象>>>type(at) #其類型是type<class'type'>表5-1列出了Python的部分類型。6.1.1類型即對象表6-1代表類型的對象位于何處(部分)6.1.1類型即對象每個對象的類型各有其獨特的性質(zhì),例如int對象不可變,list對象是可變的容器,函數(shù)對象可被調(diào)用。在需要區(qū)別的時候,我們會稱呼某類型建立的對象為“實體”,以避免混淆。使用內(nèi)置函數(shù)instance可判斷對象是否為某類型的實體。>>>callable(sq),callable(math)#sq是函數(shù),可被調(diào)用(True,False)>>>callable(list),callable(int) #list、int不僅是類型的名稱,也可被調(diào)用(True,True)>>>isinstance(sq,types.FunctionType)#sq是函數(shù)類型的實體True>>>isinstance(d,tuple) #d是tuple類型的實體True6.1.2命名空間名稱會指向?qū)ο螅臻g是名稱存在的地方,模塊、函數(shù)、類型、實體(對象)都具備命名空間的功能。importmathpi=math.pic=complex(3,4)defcircle(r)area=pi*r*rreturnarea在上述代碼中,全局范圍的命名空間里含有名稱math、pi、circle,其中math是個模塊、pi與c都是對象,都具有命名空間的功能,例如math里的sqrt、?oor、trunc都指向函數(shù),c對象里面的real與imag則指向浮點數(shù)。6.1.2命名空間函數(shù)具有命名空間的功能,但上述程序只“定義”了函數(shù)circle但尚未調(diào)用,也就是說,此時尚未產(chǎn)生circle的局部命名空間,更別提名稱r是否存在了。調(diào)用函數(shù)才會建立出局部(函數(shù))范圍的命名空間,里面會放著r、area。一旦函數(shù)結(jié)束(返回),該局部(函數(shù))范圍的命名空間也會消失,這里說的是“名稱”消失了,所以在函數(shù)之外將無法使用函數(shù)內(nèi)的名稱area,不過函數(shù)返回的對象若還有名稱指向它,就仍會繼續(xù)存活。當在程序里取用某名稱時,如果位于函數(shù)內(nèi),會先到局部(函數(shù))范圍命名空間尋找,找不到會去全局范圍命名空間尋找,若再找不到會去存放內(nèi)置名稱命名空間(由模塊__builtins__提供)尋找。在局部與全局之間還有一個“外圍函數(shù)”的范圍。

搜尋名稱時的順序規(guī)則5.2抽象數(shù)據(jù)類型序列的概念迭代器共同接口6.2

抽象數(shù)據(jù)類型前面介紹類型與對象時,一個對象看似都只有一個類型,例如3是int,[0,1,2]是list,但實際情況會更復雜。例如list、tuple、str都是序列類型,那么“序列”的類型在哪里呢?6.2.1序列的概念序列是個抽象類型,list、tuple、str都是實際類型,可產(chǎn)生出對象,但我們不能用序列類型產(chǎn)生對象,只能說類型為list(或tuple、str)的對象符合序列定義的操作接口。某些類型擁有相同的接口,例如list擁有“長度”的概念,而tuple、str也有,但int、?oat沒有。此時可把這部分的接口提出來,放在抽象類型里,讓list、tuple、str遵循并符合此抽象類型的描述。序列相關(guān)的抽象類型層級6.2.1序列的概念“長度或大小”的觀念由抽象類型Sized定義,所以符合此接口的類型對象必須含有方法__len__,從其名稱前后有兩個下劃線便可猜想,這是個具有特定意義、預先規(guī)定好名稱的方法,會被運算符或內(nèi)置函數(shù)取用。而抽象類型Container定義了“某東西是否在里面”的觀念,由__contains__負責,Sequence(序列)抽象類型“繼承”了Sized與Container,也就是說符合其定義的接口,更進一步定義了“取出元素”(__getitem__)、“某元素的索引”(index)等接口,而MutableSequence(可變序列)抽象類型又再以Sequence為基礎、加入可修改內(nèi)容的接口,諸如“移除”、“附加”、等等。這些抽象類型被放在模塊collections.abc里,可以使用內(nèi)置函數(shù)isinstance得知實體對象是否符合某類型(與抽象類型),而內(nèi)置函數(shù)issubclass可得知類型與類型之間的繼承關(guān)系(包括抽象類型)。6.2.2迭代器再來看可迭代項(Iterable)與迭代器(Iterator)。抽象類型Iterable定義了“建立Iterator”的接口,而Iterator定義了“逐步取出下一個元素”的接口,下面是個簡單的實例。>>>li=[30,41,52] #list對象,也是可迭代項>>>lit=iter(li) #以內(nèi)置函數(shù)iter取得迭代器>>>lit #名稱lit指向這個迭代器<list_iteratorobjectat0x00BD6C90>>>>next(lit) #以內(nèi)置函數(shù)next可取得下一個元素30>>>next(lit)41>>>next(lit)52>>>next(lit) #沒有下一個了,發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>StopIteration6.2.2迭代器6.2.3共同接口Python運用各種類型(包括抽象類型)來規(guī)范對象的接口,根據(jù)類型定義出一套存取方式,list、tuple、str都符合序列抽象類型,list還符合可變序列抽象類型。表5-2列出這些類型都支持的操作動作,表5-3列出可變的操作動作。除了list、tuple、str之外,還有其他類型繼承自序列抽象類型。字符串str除了這里列出的操作動作,還有很多跟“文本”相關(guān)的方法。在表5-2里,s與t代表某一種序列類型的實體對象,i、j、k則都是整數(shù)。6.2.3共同接口表6-2序列類型共同的操作動作6.2.3共同接口在表6-3里,s代表可變序列類型的實體對象,t是可迭代項。表6-3可變序列類型共同的操作動作索引、切片序列比較運算符“+”與“*”序列類型的方法淺復制與深復制6.3元素的訪問6.3

元素的訪問序列類型是一種容器(數(shù)據(jù)結(jié)構(gòu)),里面的內(nèi)容稱為元素,它們的存取方式介紹如下。6.3.1序列元素索引以方括號“[]”的索引方式來指定想存取的元素,索引值從0開始計數(shù),所以長度為n的序列,有效合法的索引值會是0到n-1。>>>li=[30,41,52,63,74,85]>>>li[0] #取得索引值0之處的對象30>>>li[5],li[len(li)-1] #以內(nèi)置函數(shù)len得知長度(85,85)>>>li[99] #超過了,發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>IndexError:listindexoutofrange#索引錯誤:列表索引值超過界限>>>s='hellopython' #字符串也能使用索引>>>s[0],s[6]('h','p')>>>s[0],s[6],s[len(s)-1]('h','p','n')6.3.1序列元素索引6.3.1序列元素索引任何序列類型,例如list、tuple、str等都可以使用索引存取方式。索引值也可以是負數(shù),如果是-i(假設i為正數(shù)),會被當作“長度-i”。假設序列對象s長度是n,那么s[-i]將等同于s[len(s)-i]。負索引值從-1(最后的元素)到-n(最前面的元素)。>>>li=[30,41,52,63,74,85] #長度是6>>>li[-1],li[len(li)-1] #此處[-1]等同于[6-1](85,85) #以[-1]存取最后的元素,比len()方便>>>li[-2],li[-5],li[-6],li[-len(li)](74,41,30,30)>>>li[0],li[-0] #注意,-0還是0,不是6–0(30,30)>>>li[-7] #超過了,發(fā)生錯誤Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>IndexError:listindexoutofrange6.3.1序列元素索引6.3.1序列元素索引如果是可變序列類型,例如list,就能以賦值語句修改其內(nèi)容,即讓該索引值轉(zhuǎn)而指向新對象。>>>li=[30,41,52,63,74,85]>>>li[0]='Amy' #修改第0個>>>li[len(li)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論