本文內容全部出自《Python基礎教程》第二版,在此分享自己的學習之路。 lxx___歡迎轉載:http://www.cnblogs.com/Marlowes/p/5320049.htmllxx___ Created on Xu Hoo 我們已經瞭解到,列表這種數據結構適合於將值組織到一個結構中,並 ...
本文內容全部出自《Python基礎教程》第二版,在此分享自己的學習之路。
______歡迎轉載:http://www.cnblogs.com/Marlowes/p/5320049.html______
Created on Xu Hoo
我們已經瞭解到,列表這種數據結構適合於將值組織到一個結構中,並且通過編號對其進行引用。在本章中,你將學到一種通過名字來引用值的數據結構。這種類型的數結構成為映射(mapping)。字典是Python中唯一內建的映射類型。字典中的值並沒有特殊的順序,但是都存儲在一個特定的鍵(Key)下。鍵可以是數字、字元串甚至是元組。
4.1 字典的使用
字典這個名稱已經給出了有關這個數據結構功能的一些提示:一方面,對於普通的書來說,都是按照從頭到尾的順序進行閱讀。如果願意,也可以快速翻到某一頁,這有點像Python的列表。另一方面,構造字典的目的,不管是現實中的字典還是在Python中的字典,都是為了可以通過輕鬆查找某個特定的詞語(鍵),從而找到它的定義(值)。
某些情況下,字典比列表更加適用,比如:
√ 表示一個游戲棋盤的狀態,每個鍵都是由坐標值組成的元組;
√ 存儲文件修改時間,用文件名作為鍵;
√ 數字電話/地址簿。
假如有一個人名列表如下:
>>> names = ["Alice", "Beth", "Cecil", "Dee-Dee", "Earl"]
如果要創建一個可以存儲這些人的電話號碼的小型資料庫,應該怎麼做呢?一種方法是建立一個新的列表。假設只存儲四位的分機電話號碼,那麼可以得到與下麵相似的列表:
>>> numbers = ["2341", "9102", "3158", "0142", "5551"]
建立了這些列表後,可以通過如下方式查找Cecil的電話號碼:
>>> numbers[names.index("Cecil")] '3158'
這樣做雖然可行,但是並不實用。真正需要的效果應該類似以下麵這樣:
>>> phonebook["Cecil"] '3158'
你猜怎麼著?如果phonebook是字典,就能像上面那樣操作了。
整數還是數字字元串
看到這裡,讀者可能會有疑問:為什麼用字元串而不用整數表示電話號碼呢?考慮一下Dee-Dee的電話號碼會怎麼樣:
>>> 0142 98
這並不是我們想要的結果,是嗎?就像第一章曾經簡略地提到的那樣,八進位數字均以0開頭。不能像那樣表示十進位數字。
>>> 0912 File "<stdin>", line 1 0912 ^ SyntaxError: invalid token
教訓就是:電話號碼(以及其他可能以0開頭的數字)應該表示為數字字元串,而不是整數。
4.2 創建和使用字典
字典可以通過下麵的方式創建:
>>> phonebook = {"Alice": "2341", "Beth": "9102", "Cecil": "3258"}
字典由多個鍵及與其對應的值構成的鍵-值對組成(我們也把鍵-值對稱為項)。在上例中,名字是鍵,電話號碼是值。每個鍵和它的值之間用冒號(:)隔開,項之間用逗號(,)隔開,而整個字典是由一對大括弧括起來。空字典(不包括任何項)由兩個大括弧組成,像這樣:{}。
註:字典中的鍵是唯一的(其他類型的映射也是如此),而值並不唯一。
4.2.1 dict函數
可以用dict函數(dict函數根本不是真正的函數,它是個類型,就像list、tuple和str一樣),通過其他映射(比如其他字典)或者(鍵,值)對的序列建立字典。
>>> items = [("name", "Gumby"), ("age", 42)] >>> d = dict(items) >>> d {'age': 42, 'name': 'Gumby'} >>> d["name"] 'Gumby'
dict函數也可以通過關鍵字參數來創建字典,如下例所示:
>>> d = dict(name="Gumby", age=42) >>> d {'age': 42, 'name': 'Gumby'}
儘管這可能是dict函數最有用的功能,但是還能以映射作為dict函數的參數,以建立其項與映射相同的字典(如果不帶任何參數,則dict函數返回一個新的空字典,就像list、tuple以及str等函數一樣)。如果另一個映射也是字典(畢竟這是唯一內建的映射類型),也可以使用本章稍後講到的字典方法copy。
4.2.2 基本字典操作
字典的基本行為在很多方面與序列(sequence)類似:
√ len(d)返回d中項(鍵-值對)的數量;
√ d[k]返回關聯到鍵k上的值;
√ d[k]=v將值v關聯到鍵k上;
√ del d[k]刪除鍵為k的項;
√ k in d檢查d中是否有含有鍵為k的項。
儘管字典和列表有很多特性相同,但也有下麵一些重要的區別。
√ 鍵類型:字典的鍵不一定為整型數據(但也可以是),鍵可以是任意的不可變類型,比如浮點型(實型)、字元串或者元組。
√ 自動添加:即使鍵起初在字典中並不存在,也可以為它賦值,這樣字典就會建立新的項。而(在不使用append方法或者其他類似操作的情況下)不能將值關聯到列表範圍之外的索引上。
√ 成員資格:表達式k in d(d為字典)查找的是鍵,而不是值。表達式v in l(l為列表)則用來查找值,而不是索引。這樣看起來好像有些不太一致,但是當習慣以後就會感覺非常自然了。畢竟,如果字典含有指定的鍵,查找相應的值也就很容易了。
註:在字典中檢查鍵的成員資格比在列表中檢查值的成員資格更高效,數據結構的規模越大,兩者的效率差距越明顯。
第一點——鍵可以是任意不可變類型——是字典最強大的地方。第二點也很重要。看看下麵的區別:
>>> x = [] # 列表 >>> x[42] = "Foobar" Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list assignment index out of range >>> x = {} # 字典 >>> x[42] = "Foobar" >>> x {42: 'Foobar'}
首先,程式試圖將字元串"Foobar"關聯到一個空列表的42號位置上——這顯然是不可能的,因為這個位置根本不存在。為了將其變為可能,我必須用[None]*43或者其他方式初始化x,而不能僅使用[]。但是下一個例子工作得很好。我將"Foobar"關聯到空字典的鍵42上,沒問題!新的項已經添加到字典中,我達到目的了。
代碼清單4-1所示是電話本例子的代碼。
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 # 一個簡單的資料庫 5 # 字典使用人名作為鍵。每個人用另一個字典來表示,其鍵"phone"和"addr"分別表示他們的電話號碼和地址。 6 7 people = { 8 9 "Alice": { 10 "phone": "2341", 11 "addr": "Foo drive 23" 12 }, 13 14 "Beth": { 15 "phone": "9102", 16 "addr": "Bar street 42" 17 }, 18 19 "Cecil": { 20 "phone": "3158", 21 "addr": "Baz avenue 90" 22 } 23 } 24 25 # 針對電話號碼和地址使用的描述性標簽,會在列印輸出的時候用到 26 labels = { 27 "phone": "phone number", 28 "addr": "address" 29 } 30 31 name = raw_input("Name: ") 32 33 # 查找電話號碼還是地址 34 request = raw_input("Phone number (p) or address (a)? ") 35 36 # 使用正確的鍵 37 if request == "p": 38 key = "phone" 39 if request == "a": 40 key = "addr" 41 42 # 如果名字是字典中的有效鍵才列印信息 43 if name in people: 44 print "%s's %s is %s." % (name, labels[key], people[name][key])Code_Listing 4-1
下麵是程式的運行示例:
Name: Beth Phone number (p) or address (a)? a Beth's address is Bar street 42.
4.2.3 字典的格式化字元串
在第三章,已經見過如何使用字元串格式化功能來格式化元組中所有的值。如果使用的是字典(只以字元串作為鍵的)而不是元組,會使字元串格式化更酷一些。在每個轉換說明符(conversion specifier)中的%字元後面,可以加上鍵(用圓括弧括起來),後面再跟上其他說明元素。
>>> phonebook {'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'} >>> "Cecil's phone number is %(Cecil)s." % phonebook "Cecil's phone number is 3258."
除了增加的字元串鍵之外,轉換說明符還是像以前一樣工作。當以這種方式使用字典的時候,只要所有給出的鍵都能在字典中找到,就可以使用任意數量的轉換說明符。這類字元串格式化在模板系統中非常有用(本例中使用HTML)。
>>> template = """<html> ... <head><title>%(title)s</title></head> ... <body> ... <h1>%(title)s</h1> ... <p>%(text)s</p> ... </body>""" >>> data = {"title": "My Home Page", "text": "Welcome to my home page!"} >>> print template % data <html> <head><title>My Home Page</title></head> <body> <h1>My Home Page</h1> <p>Welcome to my home page!</p> </body>
註:string.Template類(第三章提到過)對於這類應用也是非常有用的。
4.2.4 字典方法
就像其他內建類型一樣,字典也有方法。這些方法非常有用,但是可能不會像列表或者字元串方法那樣被頻繁地使用。讀者最好先簡單瀏覽一下本節,瞭解有哪些方法可用,然後在需要的時候再回過頭來查看特定方法的具體用法。
1.clear
clear方法清除字典中所有的項。這是個原地操作(類似於list.sort),所以無返回值(或者說返回None)。
>>> d = {} >>> d["name"] = "Gumby" >>> d["age"] = 42 >>> d {'age': 42, 'name': 'Gumby'} >>> returned_value = d.clear() >>> d {} >>> print returned_value None
為什麼這個方法有用呢?考慮以下兩種情況。
>>> x = {} # 第一種情況 >>> y = x >>> x["key"] = "value" >>> y {'key': 'value'} >>> x = {} >>> y {'key': 'value'} >>> x = {} # 第二種情況 >>> y = x >>> x["key"] = "value" >>> y {'key': 'value'} >>> x.clear() >>> y {}
兩種情況中,x和y最初對應同一個字典。情況1中,我通過將x關聯到一個新的空字典來“清空”它,這對y一點影響也沒有,它還關聯到原先的字典。這可能是所需要的行為,但是如果真的想清空原始字典中的所有的元素,必須使用clear方法。正如在情況2中所看到的,y隨後也被清空了。
2.copy
copy方法返回一個具有相同鍵-值對的新字典(這個方法實現的是淺複製(shallow copy),因為值本身就是相同的,而不是副本)。
>>> x = {"username": "admin", "machines": ["foo", "bar", "baz"]} >>> y = x.copy() >>> y["username"] = "mlh" >>> y["machines"].remove("bar") >>> y {'username': 'mlh', 'machines': ['foo', 'baz']} >>> x {'username': 'admin', 'machines': ['foo', 'baz']}
可以看到,當在副本中替換值的時候,原始字典不受影響,但是,如果修改了某個值(原地修改,而不是替換),原始的字典也會改變,因為同樣的值也存儲在原字典中(就像上面例子中的machines列表一樣)。
避免這種問題的一種方法就是使用深複製(deep copy),複製其包含的所有值。可以使用copy模塊的deepcopy函數來完成操作:
>>> from copy import deepcopy >>> d = {} >>> d["names"] = ["Alfred", "Bertrand"] >>> c = d.copy() >>> dc = deepcopy(d) >>> d["names"].append("Clive") >>> c {'names': ['Alfred', 'Bertrand', 'Clive']} >>> dc {'names': ['Alfred', 'Bertrand']}
3.fromkeys
fromkeys方法使用給定的鍵建立新的字典,每個鍵都對應一個預設的值None。
>>> {}.fromkeys(["name", "age"]) {'age': None, 'name': None}
剛纔的例子中首先構造了一個空字典,然後調用它的fromkeys方法,建立另外一個字典——有些多餘。此外,你還可以直接在dict上面調用該方法,前面講過,dict是所有字典的類型(關於類型和類的概念在第七章中會深入討論)。
>>> dict.fromkeys(["name", "age"]) {'age': None, 'name': None}
如果不想使用None作為預設值,也可以自己提供預設值。
>>> dict.fromkeys(["name", "age"], "(unknown)") {'age': '(unknown)', 'name': '(unknown)'}
4.get
get方法是個更寬鬆的訪問字典項的方法。一般來說,如果試圖訪問字典中不存在的項時會出錯:
>>> d = {} >>> print d["name"] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'name'
而用get就不會:
>>> print d.get("name") None
可以看到,當使用get訪問一個不存在的鍵時,沒有任何異常,而得到了None值。還可以自定義“預設”值,替換None:
>>> d.get("name", "N/A") 'N/A'
如果鍵存在,get使用起來就像普通的字典查詢一樣:
>>> d["name"] = "Eric" >>> d.get("name") 'Eric'
代碼清單4-2演示了一個代碼清單4-1程式的修改版本,它使用get方法訪問“資料庫”實體。
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 # 一個簡單的資料庫 5 # 字典使用人名作為鍵。每個人用另一個字典來表示,其鍵"phone"和"addr"分別表示他們的電話號碼和地址。 6 7 people = { 8 9 "Alice": { 10 "phone": "2341", 11 "addr": "Foo drive 23" 12 }, 13 14 "Beth": { 15 "phone": "9102", 16 "addr": "Bar street 42" 17 }, 18 19 "Cecil": { 20 "phone": "3158", 21 "addr": "Baz avenue 90" 22 } 23 } 24 25 # 針對電話號碼和地址使用的描述性標簽,會在列印輸出的時候用到 26 labels = { 27 "phone": "phone number", 28 "addr": "address" 29 } 30 31 name = raw_input("Name: ") 32 33 # 查找電話號碼還是地址 34 request = raw_input("Phone number (p) or address (a)? ") 35 36 # 使用正確的鍵 37 key = request # 如果請求既不是"p"也不是"a" 38 39 if request == "p": 40 key = "phone" 41 if request == "a": 42 key = "addr" 43 44 # 使用get()提供預設值 45 person = people.get(name, {}) 46 label = labels.get(key, key) 47 result = person.get(key, "not available") 48 49 print "%s's %s is %s." % (name, label, result)Code_Listing 4-2
以下是程式運行的輸出。註意get方法帶來的靈活性如何使得程式在用戶輸入我們並未準備的值時也能做出合理的反應。
Name: Gumby Phone number (p) or address (a)? batting average Gumby's batting average is not available.
5.has_key
has_key方法可以檢查字典中是否含有特定的鍵。表達式d.has_key(k)相當於表達式k in d。使用哪個方式很大程度上取決於個人的喜好。Python3.0中不包括這個函數。
下麵是一個使用has_key方法的例子:
>>> d = {} >>> d.has_key("name") False >>> d["name"] = "Eric" >>> d.has_key("name") True
6.items和iteritems
items方法將字典所有的項以列表方式返回,列表中的每一項都表示為(鍵, 值)對的形式。但是項在返回時並沒有遵循特定的次序。
>>> d = {"title": "Python Web Site", "url": "http://www.python.org", "spam": "0"} >>> d.items() [('url', 'http://www.python.org'), ('spam', '0'), ('title', 'Python Web Site')]
iteritems方法的作用大致相同,但是會返回一個迭代器對象而不是列表:
>>> it = d.iteritems() >>> it <dictionary-itemiterator object at 0x00000000029BAEF8> >>> list(it) # Convert the iterator to a list [('url', 'http://www.python.org'), ('spam', '0'), ('title', 'Python Web Site')]
在很多情況下使用iteritems會更加高效(尤其是想要迭代結果的情況下)。關於迭代器的更多信息,請參見第九章。
7.keys和iterkeys
keys方法將字典中的鍵以列表形式返回,而iterkeys則返回針對鍵的迭代器。
8.pop
pop方法用來獲得對應於給定鍵的值,然後將這個鍵-值對從字典中移除。
>>> d = {"x": 1, "y": 2} >>> d.pop("x") 1 >>> d {'y': 2}
9.popitem
popitem方法類似於list.pop,後者會彈出列表的最後一個元素。但不同的是,popitem彈出隨機的項,因為字典並沒有“最後的元素”或者其他有關順序的概念。若想一個接一個地移除並處理項,這個方法就非常有效了(因為不用首先獲取鍵的列表)。
>>> d {'url': 'http://www.python.org', 'spam': '0', 'title': 'Python Web Site'} >>> d.popitem() ('url', 'http://www.python.org') >>> d {'spam': '0', 'title': 'Python Web Site'}
儘管popitem和列表的pop方法很類似,但字典中沒有與append等價的方法。因為字典是無序的,類似於append的方法是沒有任何意義的。
10.setdefault
setdefault方法在某種程度上類似於get方法,能夠獲得與給定鍵相關聯的值,除此之外,setdefault還能在字典中不含有給定鍵的情況下設定相應的鍵值。
>>> d = {} >>> d.setdefault("name", "N/A") 'N/A' >>> d {'name': 'N/A'} >>> d["name"] = "Gumby" >>> d.setdefault("name", "N/A") 'Gumby' >>> d {'name': 'Gumby'}
可以看到,當鍵不存在的時候,setdefault返回預設值並且相應地更新字典。如果鍵存在,那麼就返回與其對應的值,但不改變字典。預設值是可選的,這點和get一樣。如果不設定,會預設使用None。
>>> d = {} >>> print d.setdefault("name") None >>> d {'name': None}
11.update
update方法可以利用一個字典項更新另外一個字典:
>>> d = { ... "title": "Python Web Site", ... "url": "http://www.python.org", ... "changed": "Mar 14 22:09:15 MET 2008" ... } >>> x = {"title": "Python Language Website"} >>> d.update(x) >>> d {'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2008', 'title': 'Python Language Website'}
提供的字典中的項會被添加到舊的字典中,若有相同的鍵則會進行覆蓋。
update方法可以使用與調用dict函數(或者類型構造函數)同樣的方式進行調用,這點在本章前面已經討論。這就意味著update可以和映射、擁有(鍵、值)對的隊列(或者其他可迭代的對象)以及關鍵字參數一起調用。
12.values和itervalues
values方法以列表的形式返回字典中的值(itervalues返回值的迭代器)。與返回鍵的列表不同的是,返回值的列表中可以包含重覆的元素:
>>> d = {} >>> d[1] = 1 >>> d[2] = 2 >>> d[3] = 3 >>> d[4] = 1 >>> d.values() [1, 2, 3, 1]
4.3 小結
本章介紹瞭如下內容。
映射:映射可以使用任意不可變對象標識元素。最常用的類型是字元串和元組。Python唯一的內建映射類型是字典。
利用字典格式化字元串:可以通過在格式化說明符中包括名稱(鍵)來對字典應用字元串格式化操作。在當字元串格式化中使用元組時,還需要對元組中每一個元素都設定“格式化說明符”。在使用字典時,所用的說明符可以比在字典中用到的項少。
字典的方法:字典有很多方法,調用的方式和調用列表以及字元串方法的方式相同。
4.3.1 本章的新函數
本章涉及的新函數如表4-1所示。
表4-1 本章的新函數
dict(seq) 用(鍵、值)對(或者映射和關鍵字參數)建立字典。
4.3.2 接下來學什麼
到現在為止,已經介紹了很多有關Python的基本數據類型的只是,並且講解瞭如何使用它們來建立表達式。那麼請回想一下第一章的內容,電腦程式還有另外一個重要的組成因素——語句。下一章我們會對語句進行詳細的討論。