字典其實和之前的元祖和列表功能相似,都是用來儲存一系列對象的。也就是一種可變容器,或者是我所比喻的革新派的菜單。
但也不是完全相同,我在之前曾經將字典稱為特殊的'序列',是字典擁有序列的部分特性,但是又不符合序列的定義。
首先我們來看下字典是如何創建的:
a = {'a':1,'b':2,'c':2} b = {} c = dict(a=1) print a print b print c
我們可以使用{} 或者dict() 來創建一個字典對象。
但字典裡面寫的是什麼?下面我來分析一下。
首先,字典是以鍵值對的形式來儲存對象的,即 key:value ,鍵和值之間用冒號分隔,而每個鍵值對之間用逗號分隔。
這個是時候可以聯想一下序列的形式,在序列中,對象之間也是用逗號分隔的,但不同的是,序列是使用索引作為key,且索引是提取定義好的,按照一定順序的。但是字典中,key是自定義的,相當於我們為對象起一個變量名,只不過變量名在字典的key中是以字符串的形式存在的,就像 c 中的創建方式,變量名為 a ,最終得到的鍵為 'a'。
那是不是字典的鍵就只能是字符串類型呢?並不是,我們也可以像序列一樣用數字作為鍵。
a = {666:'scolia',} print a
當然,為了提高代碼可讀性,我在只有單個對象的時候還是用了逗號。
元祖和列表也可以作為鍵嗎?
答案是:NO!
看來鍵要求精確單一的對象,使用容器作為鍵肯定是不行的。當然值可以是任何對象,和元祖、列表一樣。
看到這裡,我們可能回想字典無非就是高級一點的列表而已,為什麼又不是序列呢?
因為字典是無序的。
我們看變量 a 中的字典:
a = {'a':1,'b':2,'c':2}
我們在創建的時候明明是按一定順序排列的,為什麼輸出的時候順序卻亂了?
這正是字典無序性的體現。
首先序列之所以被稱為序列:正如其名,有序的、隊列式的。我們在序列中逐一放入元素的時候,會自動的按照從左到右進行編號,也就是索引,而每一個索引對應一個對象。而字典卻失去了索引的約束,用了自定義的鍵來取代,當我們在獲取對象時也是用鍵名來獲取,只要知道了鍵名,那麼對象在字典中的哪個位置也無所謂了,所以字典是無序的,也就不能稱為序列。
但我們依然可以將其想象為是一種高級的列表,只不過這個列表的索引是自定義的,無序的。
另外,當字典中的出現了同名的鍵會怎麼辦?
a = {'scolia': 1,'scolia':2,'scolia':3} print a
就像變量命名一樣,前面的都被沖突掉了。
這時又有同學問:不是說字典是無序的嗎?我怎麼知道誰在前誰在後?
我曾經把索引比作是特殊的變量名,只不過普通的變量名不能只是數字,而索引則是通過數字去內存取值。同理,字典裡的鍵也可以看作是變量名,在字典裡的元素打包成一個字典之前,先進行了變量的賦值操作,而對同一個變量進行多次賦值相當於切換其在內存的引用,只有最後一個賦值有效,這裡也是一樣的。在鍵值對創建的時候,按照我們寫時候的順序先進行賦值操作,然後保存在字典中,保存之後才是無序的。
那麼值相同的時候是否也是同一個對象呢?
a = {'a':300,'b':300} print id(a['a']),id(a['b'])
看來是的,和序列中的一樣,也是節省內存的優化。
接下來我們就可以開始學習字典的內置方法了,首先按老規矩先使用 help(dict) 查看其幫助文檔。
Help on class dict in module __builtin__: class dict(object) | dict() -> new empty dictionary | dict(mapping) -> new dictionary initialized from a mapping object's | (key, value) pairs | dict(iterable) -> new dictionary initialized as if via: | d = {} | for k, v in iterable: | d[k] = v | dict(**kwargs) -> new dictionary initialized with the name=value pairs | in the keyword argument list. For example: dict(one=1, two=2) | | Methods defined here: | | __cmp__(...) | x.__cmp__(y) <==> cmp(x,y) | | __contains__(...) | D.__contains__(k) -> True if D has a key k, else False | | __delitem__(...) | x.__delitem__(y) <==> del x[y] | | __eq__(...) | x.__eq__(y) <==> x==y | | __ge__(...) | x.__ge__(y) <==> x>=y | | __getattribute__(...) | x.__getattribute__('name') <==> x.name | | __getitem__(...) | x.__getitem__(y) <==> x[y] | | __gt__(...) | x.__gt__(y) <==> x>y | | __init__(...) | x.__init__(...) initializes x; see help(type(x)) for signature | | __iter__(...) | x.__iter__() <==> iter(x) | | __le__(...) | x.__le__(y) <==> x<=y | | __len__(...) | x.__len__() <==> len(x) | | __lt__(...) | x.__lt__(y) <==> x<y | | __ne__(...) | x.__ne__(y) <==> x!=y | | __repr__(...) | x.__repr__() <==> repr(x) | | __setitem__(...) | x.__setitem__(i, y) <==> x[i]=y | | __sizeof__(...) | D.__sizeof__() -> size of D in memory, in bytes | | clear(...) | D.clear() -> None. Remove all items from D. | | copy(...) | D.copy() -> a shallow copy of D | | fromkeys(...) | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v. | v defaults to None. | | get(...) | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None. | | has_key(...) | D.has_key(k) -> True if D has a key k, else False | | items(...) | D.items() -> list of D's (key, value) pairs, as 2-tuples | | iteritems(...) | D.iteritems() -> an iterator over the (key, value) items of D | | iterkeys(...) | D.iterkeys() -> an iterator over the keys of D | | itervalues(...) | D.itervalues() -> an iterator over the values of D | | keys(...) | D.keys() -> list of D's keys | | pop(...) | D.pop(k[,d]) -> v, remove specified key and return the corresponding value. | If key is not found, d is returned if given, otherwise KeyError is raised | | popitem(...) | D.popitem() -> (k, v), remove and return some (key, value) pair as a | 2-tuple; but raise KeyError if D is empty. | | setdefault(...) | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D | | update(...) | D.update([E, ]**F) -> None. Update D from dict/iterable E and F. | If E present and has a .keys() method, does: for k in E: D[k] = E[k] | If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v | In either case, this is followed by: for k in F: D[k] = F[k] | | values(...) | D.values() -> list of D's values | | viewitems(...) | D.viewitems() -> a set-like object providing a view on D's items | | viewkeys(...) | D.viewkeys() -> a set-like object providing a view on D's keys | | viewvalues(...) | D.viewvalues() -> an object providing a view on D's values | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | __hash__ = None | | __new__ = <built-in method __new__ of type object> | T.__new__(S, ...) -> a new object with type S, a subtype of Tdict
按照其功能可分為:
1.大於、小於、等於等比較運算符
2.成員判斷
3.內置函數相關的(另起一篇總結)
4.增刪改查相關操作
1.比較運算符
因為字典不是序列了,所以就不能像序列一樣按照索引逐個打擂台了。
不過大小的比較規則還是一樣的,只是上場的順序未知而已。
那到底比較的時候順序是怎麼樣的,看下面的例子。
a = {'a':1,'b':2,'c':3} for x in a: print x
我們使用for循環來看看比較時的順序,可以看出,順序是無可預估的,雖然這裡只有三個鍵值對,貌似有一定的規律,但是一旦裡面的對象多起來,就得不出規律了,而且不可能每次比較的時候都要自己看一下裡面是怎麼比的,那樣實在太累,所以我們得出一個結論:少用字典間的比較,要比較用序列去。
另外這裡補充一點:當我們使用for循環時,我們得到的是鍵名,如果我們要取到值怎麼辦?可以像下面這樣寫:
a = {'a':1,'b':2,'c':3} for x in a: print a[x]
當然還有其他寫法,看完內置方法後自己總結吧。
2.成員判斷
1. in
在序列中判斷裡面有沒有某個元素時使用的是 in ,但細心的同學會發現,在幫助文檔中並沒有出現 in ,是不是in不能用了呢?
看下面這個例子:
a = {'a':1,'b':2, 3:30} b = 2 print b in a print 2 in a print 'b' in a print 3 in a
還可以使用,但是這裡 in 只能判斷字典裡是否有哪個鍵,而不是鍵對應的值。
2. D.has_key(k) -> True if D has a key k, else False
和 in 一樣,判斷裡面是否有某個鍵,返回布爾值。
a = {'a':1,'b':2, 3:30} print a.has_key('b') print a.has_key(3)
3. D.__contains__(k) -> True if D has a key k, else False
判斷是否有某個鍵,和上面的一樣,就不多說明了。
4.判斷字典裡面是否有某個對象。
字典中並沒有內置的方法能直接判斷,但是我們可以自己寫一個。
a = {'a':1,'b':2, 3:30} b = 30 c = a.values() #返回一個由字典的值組成的列表 print c #同樣我們無法預估字典裡的順序 print b in c
我們轉了一個彎,這就要考驗思維的靈活性了。
3.增
1. x.__setitem__(i, y) <==> x[i]=y
這個和列表中的不同,列表的這個寫法是修改元素,如果給的索引不存在是會報錯的。但在字典中,這個方法除了可以用來修改元素之外,如果給定的鍵不存在,則會在字典中創建這個鍵值對。
a = {'a':1,'b':2, 3:30} a['scolia'] = 'good' print a
2. dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v. v defaults to None.
這個方法用於創建新的字典對象,我這裡將其歸類到這裡。
其中,S 為一個鍵的序列,v為值,將為S所指定的所有鍵設置同一個值,默認為None。
a = dict.fromkeys(('scolia'),123) b = dict.fromkeys(('scolia',)) print a print b
注意S的寫法,這就是為什麼在元祖只有一個元素的時候一定要加一個逗號,否則解釋器會將其當作傳參,將字符串傳進去,而字符串也是一個序列,所以沒一個字符都被當做一個鍵,就造成了a的輸出了。將S寫成一個列表就不會有這樣的錯誤,但元祖消耗的資源少,具體選擇看個人。
a = dict.fromkeys(['scolia'],123) print a
a = {'scolia': 1,'good':1,1:0} print a.fromkeys(('k'),123) print a
這裡很特殊,創建的對象並沒有添加到原字典中,而是所為一個返回值。
3. D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
這是一個集查詢、修改、增加鍵值對的方法。
a = dict.fromkeys(['scolia'],123) print a.setdefault('s',123) print a
這裡解釋這個方法的細節:首先k是必須的參數,如果只給k一個參數,則d默認為None,也可以指定d的值。
a = dict.fromkeys(['scolia'],123) print a.setdefault('scolia') print a.setdefault('s') print a
其執行順序為:
1.先用給定的k看看裡面有沒有對應的鍵,有就返回這個鍵的值。
2.如果沒有k這個鍵,則創建這個鍵,然後用d作為其值,如果d沒給就默認為None(空對象)。
4.刪
1. del x[y]
用del關鍵字刪除對象,在列表中講過,只不過y從索引變成了鍵名。這裡不再重復。
2. D.clear() -> None. Remove all items from D.
刪除整個字典裡的元素,返回值為None。
a ={'scolia':123} print a.clear() print a
不需要傳參,但要注意就算清空了字典裡的所以元素,最後得到的還是一個空字典,而不是空對象None。
3. D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
刪除指定的鍵值對,並返回相應的值。如果沒找到鍵返回d,若連d都沒給,則觸發KeyError錯誤。
a ={'scolia':123,123:321} print a.pop(123) print a.pop(666,'沒找到') print a.pop(999)
4. D.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if D is empty.
刪除字典中的第一個元素,並將其按一個元祖返回,如果字典為空則觸發KeyError錯誤
a ={'scolia':123,123:321,555:777} print a print a.popitem() print a
但還是那句話,字典中元素的順序是未知的,誰知道第一個是什麼,所以多配合迭代循環使用,逐個刪除掉。
5.查
1. D.keys() -> list of D's keys
返回一個由鍵組成的列表
a ={'scolia':123,123:321,555:777} print a.keys()
2. D.values() -> list of D's values
返回一個由值組成的列表
a ={'scolia':123,123:321,555:777} print a.values()
3. D.items() -> list of D's (key, value) pairs, as 2-tuples
返回一個由鍵值組成的元祖的列表。
a ={'scolia':123,123:321,555:777} print a.items()
4. D.iterkeys() -> an iterator over the keys of D
返回一個包含所有鍵的迭代對象
a ={'scolia':123,123:321,555:777} b = a.iterkeys() for x in b: print x
5. D.itervalues() -> an iterator over the values of D
返回一個包含所有值的迭代對象,原理同上,不再詳細說明。
6. D.iteritems() -> an iterator over the (key, value) items of D
返回一個包含鍵值對元祖的迭代對象,同理。另外注意下元祖的迭代循環技巧。
7. D.viewkeys() -> a set-like object providing a view on D's keys
返回一個鍵的類似集合的對象。集合的作用是去除重復,但字典對於鍵的重復已經有相應處理,感覺意義不大。
a = {'scolia': 1,'scolia':2,1:123} print a.viewkeys() print type(a.viewkeys())
8. D.viewvalues() -> an object providing a view on D's values
返回一個值的視圖對象,注意不是集合對象,沒有去重作用。
a = {'scolia': 1,'good':1,1:123} print a.viewvalues() print type(a.viewvalues())
9. D.viewitems() -> a set-like object providing a view on D's items
返回一個由鍵和值組成的元祖的類集合對象。
a = {'scolia': 1,'good':1,1:123} print a.viewitems() print type(a.viewitems())
同樣感覺意義不大,原因同上。
6.改
1. x[i]=y
和列表中的類似,只是 i 不是索引而是鍵名,當鍵名不存在時,就相當於新增鍵值對了。這裡不重復說明了。
2. D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
上面講過了,不再重復
3. D.update([E, ]**F) -> None.
從字典或可迭代對象E和F中,更新元素到字典D中。這裡其實也可以歸類到增加對象那裡去,但字典的增操作和改操作其實很多方法是可以相通的,這裡就放到這裡了,讓這裡看起來不會太單薄。
這裡的**F的寫法其實是解決冗余參數的寫法,詳細我們會在函數中講。
當E .key() 有方法時。
for k in E: D[k] = E[k]
即當E也是字典時,我們可以將一個字典添加到另一個字典中:
a = {'scolia': 1,'good':1} b = {'scolia':2, 666:999} a.update(b) print a
print b
a中的同名鍵會被b的覆蓋掉,且不會影響b本身。
當E沒有.key()方法時。
for (k, v) in E: D[k] = v
即E為二元元祖序列,以二元元祖中的第一個元素為鍵,第二個元素為值,添加到字典中。
a = {'scolia': 1,'good':1,1:0} b = [('a',123),('b',321),(1,1)] a.update(b) print a
所謂二元元祖序列,即裡面放多個元祖,每個元祖只有兩個元素,而其外層為序列,元祖、列表都行。
注意:有些同學在學完函數後可能會糾結,**F其實也算是一個字典,那我傳進去的字典究竟是傳給了誰?
首先**F是處理冗余參數的,要想有作用,首先參數要超過需要的,而當我們傳一個字典或其他對象的時候,是傳給了E,當傳多個對象的時候,多出來的就到了**F中了,而**F中的對象這裡並沒有做處理,就相當於多出來的就扔到垃圾桶裡去了。