7.1 字典:當索引不好用時
有天你想翻開《新華字典》,查找下“龜”是不是一種鳥。如果是按拼音檢索,你總不可能從字母a開始查找吧?你應該直接翻到字母g在字典中的位置,然後接著找到gui的發音,繼而找到“龜”字的釋義:廣義上指龜鱉目的統稱,狹義上指龜科下的物種。
圖7-1 映射
在Python中也有字典,就拿剛才的例子來說,Python的字典把這個字(或單詞)稱為“鍵(key)”,把其對應的含義稱為“值(value)”。另外值得提一下的是,Python的字典在有些地方稱為雜湊(hash),有些地方稱為關係陣列,其實這些都跟今天要講的Python字典是同一個概念。
字典是Python中唯一的映射類型,映射是數學上的一個術語,指兩個元素集之間元素相互“對應”的關係,如圖7-1所示。
映射類型區別於序列類型,序列類型以陣列的形式存儲,通過索引的方式來獲取相應位置的值,一般索引值與對應位置存儲的資料是毫無關係的。舉個例子:
清單brand、slogan的索引和相對的值是沒有任何關係的,可以看出,唯一有聯繫的就是兩個清單間,索引號相同的元素是有關係的(品牌對應口號嘛),所以這裡通過“brand.index('魚C工作室')”這樣的語句,間接地實現通過品牌查找對應的口號的功能。
這確實是一種可實現方法,但用起來多少有些彆扭,而且效率還不高。況且Python是以簡潔為主,這樣的實現肯定是差強人意的。所以,需要有字典這種映射類型的出現。
7.1.1 創建和訪問字典
先演示一下用法:
字典的使用非常簡單,它有自己的標誌性符號,就是用大括弧({})定義。字典由多個鍵及其對應的值共同構成,每一對鍵值組合稱為項。在剛才的例子中,“李寧”、“耐克”、“愛迪達”、“魚C工作室”這些品牌就是鍵,而“一切皆有可能”、“Just do it”、“Impossible
is nothing”、“讓程式設計改變世界”這些口號就是對應的值。眼尖的讀者可能已經發現了:字典中的項跟創建的順序是不一樣的?沒錯,字典跟序列不同,序列講究順序,字典講究映射,不講順序。
另外需要注意的是:字典的鍵必須獨一無二,而值可以取任何資料類型,但必須是不可變的(如字串、數或元組)。
要聲明一個空字典,直接用個大括弧即可:
有讀者朋友可能會問,為什麼上面的例子中這麼多括弧?
因為dict()函數的參數可以是一個序列(但不能是多個),所以要打包成一個元組序列(列表也可以)。當然,如果嫌上面的做法太麻煩,還可以通過提供具有映射關係的參數來創建字典:
這裡要注意的是鍵的位置不能加上字串的引號,否則會報錯:
還有一種創建方法是直接給字典的鍵賦值,如果鍵存在,則改寫鍵對應的值;如果不存在,則創建一個新的鍵並賦值:
正所謂殊途同歸,下面列舉的五種方法都是創建同樣的字典,請大家仔細體會下:
7.1.2 各種內置方法
字典是Python中唯一的映射類型,字典不是序列。如果在序列中試圖為一個不存在的位置賦值的時候,會報錯;但是如果是在字典中,會自動創建相應的鍵並添加對應的值進去。
1.fromkeys()
fromkeys()方法用於創建並返回一個新的字典,它有兩個參數:第一個參數是字典的鍵;第二個參數是可選的,是傳入鍵對應的值。如果不提供,那麼默認是None。舉個例子:
上面最後一個例子告訴我們做事不能總是想當然,有時候現實會給你狠狠的一棒。fromkeys()方法並不會將值"one"、"two"和"three"分別賦值鍵1、2和3,因為fromkeys()把("one","two","three")當成一個值了。
2.keys()、values()和items()
訪問字典的方法有keys()、values()和items()。
keys()用於返回字典中的鍵,values()用於返回字典中所有的值,那麼items()當然就是返回字典中所有的鍵值對(也就是項)啦。舉個例子:
字典可以很大,有些時候我們並不知道提供的項是否在字典中存在,如果不存在,Python就會報錯:
對於代碼調試階段,報錯讓程式師及時發現程式存在的問題並修改之。但是如果程式面向使用者了,那麼經常報錯的程式肯定會被使用者所遺棄……
3.get()
get()方法提供了更寬鬆的方式去訪問字典項,當鍵不存在的時候,get()方法並不會報錯,只是默默地返回了一個None,表示啥都沒找到:
如果希望找不到資料時返回指定的值,那麼可以在第二個參數設置對應的默認返回值:
如果不知道一個鍵是否在字典中,那麼可以使用成員資格操作符(in或not in)來判斷:
在字典中檢查鍵的成員資格比序列更高效,當資料規模相當大的時候,兩者的差距會很明顯(注:因為字典是採用雜湊的方法一對一找到成員,而序列則是採取反覆運算的方式逐個比對)。最後要注意的一點是,這裡查找的是鍵而不是值,但是在序列中查找的是元素的值而不是元素的索引。
如果需要清空一個字典,則使用clear()方法:
有的讀者可能會使用變數名賦值為一個空字典的方法來清空字典,這樣做存在一定的弊端。舉個例子跟大家說說兩種清除方法有什麼不同:
從上面的例子中可以看到,a、b指向同一個字典,然後試圖通過將a重新指向一個空字典來達到清空的效果時,我們發現原來的字典並沒有被真正清空,只是a指向了一個新的空字典而已。所以這種做法在一定條件下會留下安全隱患(例如,帳戶的資料和密碼等資料有可能會被竊取)。
推薦的做法是使用clear()方法:
4.copy()
copy()方法是複製字典:
5.pop()和popitem()
接下來講講pop()和popitem(),pop()是給定鍵彈出對應的值,而popitem()是彈出一個項,這兩個比較容易:
setdefault()方法和get()方法有點相似,但是setdefault()在字典中找不到相應的鍵時會自動添加:
6.update()
最後一個是update()方法,可以利用它來更新字典:
還記得在6.2節的末尾我們埋下了一個伏筆,在末尾講到收集參數的時候,我們說Python還有另一種收集方式,就是用兩個星號(**)表示。兩個星號的收集參數表示為將參數們打包成字典的形式,現在講到了字典,就順理成章地給大家講講吧。
收集參數其實有兩種打包形式:一種是以元組的形式打包,另一種則是以字典的形式打包:
當參數帶兩個星號(**)時,傳遞給函數的任意個key=value實參會被打包進一個字典中。那麼有打包就有解包,來看一個例子:
7.2 集合:在我的世界裡,你就是唯一
上節講了Python中的字典,Python的字典是對數學中映射概念支援的直接體現。而今天呢,我們請來了字典的表親:集合。
喔?難道它們長得很像?來,大家看下代碼:
你們確實沒有眼花,在Python3裡,如果用大括弧括起一堆數字但沒有體現映射關係,那麼Python就會認為這堆玩意兒就是個集合。
那集合有什麼特色呢?不知道大家有沒有注意到本節的標題——“集合:在我的世界裡,你就是唯一”?
好,說回主題,集合在Python中幾乎起到的所有作用就是兩個字:唯一。舉個例子:
大家看到,我們根本不需要做什麼,集合就會幫我們把重複的資料清理掉,這樣是不是很方便呢?但要注意的是,集合是無序的,也就是你不能試圖去索引集合中的某一個元素:
7.2.1 創建集合
創建集合有兩種方法:一種是直接把一堆元素用大括弧({})括起來;另一種是用set()。
現在要求去除列表[1,2,3,4,5,5,3,1,0]中重複的元素。如果還沒有學習過集合,你可能會這麼寫:
當你學習了集合之後,就可以這麼幹:
看,知識才是第一生產力!不過大家發現沒有?由於set()創造了的集合內部是無序的,所以再調用list()將無序的集合轉換成清單就不能保證原來的清單的順序了(這裡Python好心辦壞事兒,把0放到前邊了),所以如果關注清單中元素的前後順序問題,使用set()這個函數時就要提高警惕啦!
7.2.2 訪問集合
由於集合中的元素是無序的,所以並不能像序列那樣用下標來進行訪問,但是可以使用反覆運算把集合中的資料一個個讀取出來:
當然也可以使用in和not in判斷一個元素是否在集合中已經存在:
使用add()方法可以為集合添加元素,使用remove()方法可以刪除集合中已知的元素:
7.2.3 不可變集合
有些時候希望集合中的資料具有穩定性,也就是說,像元組一樣不能隨意地增加或刪除集合中的元素。那麼我們可以定義不可變集合,這裡使用的是frozenset()函數,沒錯,就是把元素給frozen(冰凍)起來:
































0 留言:
發佈留言