Dict和Set類型

再議不可變對象

上面我們講了,str是不變對象,而list是可變對象。

對於可變對象,比如list,對list進行操作,list內部的內容是會變化的,比如:

>>> a = ['c', 'b', 'a']

>>> a.sort()

>>> a

['a', 'b', 'c']

而對於不可變對象,比如str,對str進行操作呢:

>>> a = 'abc'

>>> a.replace('a', 'A')

'Abc'

>>> a

'abc'

雖然字符串有個replace()方法,也確實變出了'Abc',但變量a最後仍是'abc',應該怎麼理解呢?

我們先把代碼改成下面這樣:

>>> a = 'abc'

>>> b = a.replace('a', 'A')

>>> b

'Abc'

>>> a

'abc'

要始終牢記的是,a是變量,而'abc'纔是字符串對象!有些時候,我們經常說,對象a的內容是'abc',但其實是指,a本身是一個變量,它指向的對象的內容纔是'abc'

當我們調用a.replace('a', 'A')時,實際上調用方法replace是作用在字符串對象'abc'上的,而這個方法雖然名字叫replace,但卻沒有改變字符串'abc'的內容。相反,replace方法創建了一個新字符串'Abc'並返回,如果我們用變量b指向該新字符串,就容易理解了,變量a仍指向原有的字符串'abc',但變量b卻指向新字符串'Abc'了:

所以,對於不變對象來說,調用對象自身的任意方法,也不會改變該對象自身的內容。相反,這些方法會創建新的對象並返回,這樣,就保證了不可變對象本身永遠是不可變的。

小結

使用key-value存儲結構的dictPython中非常有用,選擇不可變對象作爲key很重要,最常用的key是字符串。

tuple雖然是不變對象,但試試把(1, 2,3)(1,[2, 3])放入dictset中,並解釋結果。

 

1什麼是dict

我們已經知道,list tuple 可以用來表示順序集合,例如,班裏同學的名字:

['Adam','Lisa', 'Bart']

或者考試的成績列表:

[95, 85, 59]

但是,要根據名字找到對應的成績,用兩個 list 表示就不方便。

如果把名字和分數關聯起來,組成類似的查找表:

'Adam' ==>95

'Lisa' ==>85

'Bart' ==>59

給定一個名字,就可以直接查到分數。

Python dict 就是專門幹這件事的。用 dict 表示名字”-“成績的查找表如下:

d = {

    'Adam': 95,

    'Lisa': 85,

    'Bart': 59

}

我們把名字稱爲key,對應的成績稱爲valuedict就是通過 key來查找 value

花括號 {} 表示這是一個dict,然後按照 key: value, 寫出來即可。最後一個 key: value 的逗號可以省略。

由於dict也是集合,len() 函數可以計算任意集合的大小:

>>>len(d)

3

注意: 一個 key-value 算一個,因此,dict大小爲3

 

2訪問dict

我們已經能創建一個dict,用於表示名字和成績的對應關係:

d = {

    'Adam': 95,

    'Lisa': 85,

    'Bart': 59

}

那麼,如何根據名字來查找對應的成績呢?

可以簡單地使用 d[key] 的形式來查找對應的 value,這和 list 很像,不同之處是,list 必須使用索引返回對應的元素,而dict使用key

>>>print d['Adam']

95

>>>print d['Paul']

Traceback (mostrecent call last):

  File "index.py", line 11, in<module>

    print d['Paul']

KeyError:'Paul'

注意: 通過key 訪問 dict value,只要 key 存在,dict就返回對應的value。如果key不存在,會直接報錯:KeyError

要避免 KeyError 發生,有兩個辦法:

一是先判斷一下 key 是否存在,用 in 操作符:

if 'Paul' in d:

    print d['Paul']

如果 'Paul' 不存在,if語句判斷爲False,自然不會執行 print d['Paul'] ,從而避免了錯誤。

二是使用dict本身提供的一個 get 方法,在Key不存在的時候,返回None

>>>print d.get('Bart')

59

>>>print d.get('Paul')

None

 

3dict的特點

dict的第一個特點是查找速度快,無論dict10個元素還是10萬個元素,查找速度都一樣。而list的查找速度隨着元素增加而逐漸下降。

不過dict的查找速度快不是沒有代價的,dict的缺點是佔用內存大,還會浪費很多內容list正好相反,佔用內存小,但是查找速度慢。

由於dict是按 key 查找,所以,在一個dict中,key不能重複

dict的第二個特點就是存儲的key-value序對是沒有順序的!這和list不一樣:

d = {

    'Adam': 95,

    'Lisa': 85,

    'Bart': 59

}

當我們試圖打印這個dict時:

>>>print d

{'Lisa': 85,'Adam': 95, 'Bart': 59}

打印的順序不一定是我們創建時的順序,而且,不同的機器打印的順序都可能不同,這說明dict內部是無序的,不能用dict存儲有序的集合。

dict的第三個特點是作爲 key 的元素必須不可變Python的基本類型如字符串、整數、浮點數都是不可變的,都可以作爲 key。但是list是可變的,就不能作爲 key

可以試試用list作爲key時會報什麼樣的錯誤。

不可變這個限制僅作用於keyvalue是否可變無所謂:

{

    '123': [1, 2, 3],  # key strvaluelist

    123: '123', # key intvalue str

    ('a', 'b'): True  # key tuple,並且tuple的每個元素都是不可變對象,value boolean

}

最常用的key還是字符串,因爲用起來最方便。

 

4更新dict

dict是可變的,也就是說,我們可以隨時往dict中添加新的 key-value。比如已有dict

d = {

    'Adam': 95,

    'Lisa': 85,

    'Bart': 59

}

要把新同學'Paul'的成績 72 加進去,用賦值語句:

>>>d['Paul'] = 72

再看看dict的內容:

>>>print d

{'Lisa': 85,'Paul': 72, 'Adam': 95, 'Bart': 59}

如果 key 已經存在,則賦值會用新的 value 替換掉原來的 value

>>>d['Bart'] = 60

>>>print d

{'Lisa': 85,'Paul': 72, 'Adam': 95, 'Bart': 60}

 

5遍歷dict

由於dict也是一個集合,所以,遍歷dict和遍歷list類似,都可以通過 for 循環實現。

直接使用for循環可以遍歷 dict key

>>> d= { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

>>>for key in d:

...     print key

...

Lisa

Adam

Bart

由於通過 key 可以獲取對應的 value,因此,在循環體內,可以獲取到value的值。

 

6什麼是set

dict的作用是建立一組 key 和一組 value 的映射關係,dictkey是不能重複的。

有的時候,我們只想要 dict key,不關心 key 對應的 value,目的就是保證這個集合的元素不會重複,這時,set就派上用場了。

set 持有一系列元素,這一點和 list 很像,但是set的元素沒有重複,而且是無序的,這點和 dict key很像。

創建 set 的方式是調用 set() 並傳入一個 listlist的元素將作爲set的元素:

>>> s= set(['A', 'B', 'C'])

可以查看 set 的內容:

>>>print s

set(['A', 'C','B'])

請注意,上述打印的形式類似 list但它不是 list,仔細看還可以發現,打印的順序和原始 list 的順序有可能是不同的,因爲set內部存儲的元素是無序的。

因爲set不能包含重複的元素,所以,當我們傳入包含重複元素的 list 會怎麼樣呢?

>>> s= set(['A', 'B', 'C', 'C'])

>>>print s

set(['A', 'C','B'])

>>>len(s)

3

結果顯示,set會自動去掉重複的元素,原來的list4個元素,但set只有3個元素。

 

7訪問set

由於set存儲的是無序集合,所以我們沒法通過索引來訪問。

訪問 set中的某個元素實際上就是判斷一個元素是否在set中。

例如,存儲了班裏同學名字的set

>>> s= set(['Adam', 'Lisa', 'Bart', 'Paul'])

我們可以用 in 操作符判斷:

Bart是該班的同學嗎?

>>>'Bart' in s

True

Bill是該班的同學嗎?

>>>'Bill' in s

False

bart是該班的同學嗎?

>>>'bart' in s

False

看來大小寫很重要,'Bart' 'bart'被認爲是兩個不同的元素

 

8 set的特點

set的內部結構和dict很像,唯一區別是不存儲value,因此,判斷一個元素是否在set中速度很快。

set存儲的元素和dictkey類似,必須是不變對象,因此,任何可變對象是不能放入set中的。

最後,set存儲的元素也是沒有順序的。

set的這些特點,可以應用在哪些地方呢?

星期一到星期日可以用字符串'MON', 'TUE', ... 'SUN'表示。

假設我們讓用戶輸入星期一至星期日的某天,如何判斷用戶的輸入是否是一個有效的星期呢?

可以用 if 語句判斷,但這樣做非常繁瑣:

x = '???' # 用戶輸入的字符串

if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':

    print 'input error'

else:

    print 'input ok'

注意:if 語句中的...表示沒有列出的其它星期名稱,測試時,請輸入完整。

如果事先創建好一個set,包含'MON' ~ 'SUN'

weekdays =set(['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'])

再判斷輸入是否有效,只需要判斷該字符串是否在set中:

x = '???' # 用戶輸入的字符串

if x in weekdays:

    print 'input ok'

else:

    print 'input error'

這樣一來,代碼就簡單多了。

 

9遍歷set

由於 set 也是一個集合,所以,遍歷 set 和遍歷 list 類似,都可以通過 for 循環實現。

直接使用 for 循環可以遍歷 set 的元素:

>>> s= set(['Adam', 'Lisa', 'Bart'])

>>> for name in s:

...     print name

...

Lisa

Adam

Bart

注意觀察 for 循環在遍歷set時,元素的順序和list的順序很可能是不同的,而且不同的機器上運行的結果也可能不同。

 

8 更新set

由於set存儲的是一組不重複的無序元素,因此,更新set主要做兩件事:

一是把新的元素添加到set中,二是把已有元素從set中刪除。

添加元素時,用setadd()方法:

>>> s= set([1, 2, 3])

>>>s.add(4)

>>> prints

set([1, 2, 3,4])

如果添加的元素已經存在於set中,add()不會報錯,但是不會加進去了:

>>> s= set([1, 2, 3])

>>>s.add(3)

>>>print s

set([1, 2, 3])

刪除set中的元素時,用setremove()方法:

>>> s= set([1, 2, 3, 4])

>>>s.remove(4)

>>>print s

set([1, 2, 3])

如果刪除的元素不存在set中,remove()會報錯:

>>> s= set([1, 2, 3])

>>>s.remove(4)

Traceback (mostrecent call last):

  File "<stdin>", line 1, in<module>

KeyError: 4

所以用add()可以直接添加,而remove()前需要判斷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章