Python進階-簡單數(shù)據(jù)結構

本文為《爬著學Python》系列第九篇文章。


從現(xiàn)在開始算是要進入“真刀真槍”的Python學習了。之所以這么說,是因為學完Python進階模塊以后,基本上就具備了一定的Python編程能力了??梢哉f我們可以用不那么聰明的方法實現(xiàn)我們要實現(xiàn)的一切功能,而更高級的技巧則會在Python精進中介紹。

本文的目的在于介紹Python的內置簡單數(shù)據(jù)結構,重點是列表list字典dict,也會簡單介紹元祖tuple和集合set。我所說的“簡單”,不是指這些數(shù)據(jù)結構學習起來難度低、或者說功能比較單一(其實相反),而是說這些數(shù)據(jù)結構在Python中最常用,形式最簡單,而我們要介紹的又是他們的一些簡單用法。

列表 list

任何了解過數(shù)據(jù)結構的人都應該明白,列表永遠是應用最廣泛,功能最多樣化的數(shù)據(jù)結構,這樣的數(shù)據(jù)結構是不應該稱之為“簡單”的。Python中的列表尤是如此。Python中的列表是一種分離式動態(tài)順序表,可以實現(xiàn)非常多的數(shù)據(jù)結構,如可以直接作為,可以實現(xiàn)隊列,可以直接表示完全二叉樹所以可以作為使用,可以嵌套表示一般形結構,用自定義類進行簡單改造能更加高效地實現(xiàn)以上的以及其他許多功能……

在介紹完列表的基礎知識以后,會對上面這些結構的實現(xiàn)進行簡單示例。

創(chuàng)建列表

Python中對于列表的使用就像普通變量一樣,不需要變量聲明,直接將列表元素用方括號[]框起來進行賦值即可。例如

>>> sample1 = [1, '2', 'woo!']

需要注意的是Python的列表可以同時接受不同類型的元素,非常寬容,無論是數(shù)字、布爾值還是字符串、其他變量、其他數(shù)據(jù)結構,甚至是函數(shù),只要是對象,都可以作為列表元素保存在列表中。這也給Python列表操作帶來了各種可能性,應用場景中充分發(fā)揮想象力可以使代碼非常優(yōu)雅簡潔。

另外,我們可以對一切可迭代對象使用內置函數(shù)list()來獲得一個新的列表(在下一篇自定義函數(shù)文中,我們會仔細討論為什么說這是個新列表)。

>>> sample2 = list(sample1)

對于一般可迭代對象,我們還可以用列表生成器來完成生成列表的工作(關于迭代器和生成器會在更靠后的文章中進行介紹)。

>>> sample3 = [i for i in range(10) if i%2==0]

至此我們介紹了三種新建列表的方法:

  • 一般賦值語句
  • list() 函數(shù)轉換可迭代對象
  • 列表生成器

在文末介紹元祖以后,我們會認識到其實這些方法都是殊途同歸的。

列表的一些基本操作

我們提到過Python的內置列表是一種分離式動態(tài)順序表,我們可以先不用知道這是什么意思,只要知道這代表Python列表是可索引的而且可以幾乎無限制地往里面插入數(shù)據(jù)(如果以后確定開數(shù)據(jù)結構模塊內容,會介紹實現(xiàn)方式)。

索引和切片

所謂的索引,可以理解為Python的列表元素按順序有自己的“號碼“,我們可以通過這個號碼來直接訪問Python的列表元素。

>>> sample1 = [1, '2', 'woo!']
>>> elem1 = sample1[2]
>>> elem1
'woo!'

注意到我們可以把這個列表元素對象通過賦值直接傳給變量。而且,我們索引的號碼是[2],得到的卻是列表的第三個元素。原因是,列表元素的索引是從0開始的,它代表列表的第一個元素。而出現(xiàn)這種現(xiàn)象的原因,是因為“傳統(tǒng)”。在許多較早出現(xiàn)的語言如C語言中,定義列表變量類型時需要聲明,聲明時有時候需要聲明它的容量,這時候一般會寫成int a[10],這表示聲明一個能保存10個整數(shù)的列表。這時候再去試圖用a[10]來訪問其中的元素顯然是會產(chǎn)生歧義的,事實上a[10]在C中代表這個列表本身。為了消除歧義也為了計算機處理方便,從0開始的索引就應運而生了,這其實也算是非常巧妙的設計,是工程師智慧的結晶。

在Python中雖然已經(jīng)沒有這樣做的必要了,但還是遵循了這一傳統(tǒng),這也是為了方便早期的程序員更快地適應這種新語言。因此我們在以后也可能會看到有些Python面向對象程序設計會把列表的第一個元素設置為a[0]=0,然后用一個變量來保存真正在列表后面添加的元素個數(shù),可以先定義currentsize = 0每次插入數(shù)據(jù)就currentsize += 1,或者每次需要訪問長度時currentsize = len(a)-1。這是為了讓列表更加符合邏輯而且在某些條件判斷處理上有許多優(yōu)勢,否則也可以用a[0]來保存列表的真實長度。GitHub上有一部分Python程序員會采用這種方法,初次接觸不要奇怪人家為什么要這么做。

好吧,說了這么多只是為了加深印象,列表元素的索引是從0開始的。另外一點要說的是,Python列表接受負數(shù)作為參數(shù)從列表尾端訪問元素,不一樣的是,列表尾端第一個元素的索引是-1,后面以此類推。

>>> sample1 = [1, '2', 'woo!']
>>> elem1 = sample1[-2]
>>> elem1
'2'

接下來講列表元素的切片。在知道了索引的基礎上,切片非常好理解。索引是訪問列表中的一個元素,而切片則是訪問列表中的一段元素。

>>> sample3 = [i for i in range(10) if i%2==0]
>>> slice1 = sample3[1:3]
>>> print(slice1)
[2, 4]
>>> print(type(slice1))
<class 'list'>

注意到切片返回的是一個列表。列表切片可以接受兩個參數(shù),一個作為起點,一個作為終點,其中終點不包括在切片范圍中。這種特性是不是有點像range函數(shù)呢?其實,列表切片還有一些其他技巧。

>>> sample3 = [i for i in range(10) if i%2==0]
>>> sample3[1:]
[2, 4, 6, 8]
>>> sample3[:-2]
[0, 2, 4]
>>> sample3[::-1]
[8, 6, 4, 2, 0]

注意到

  • 切片時可以略去某端的參數(shù),代表取到這一端的列表盡頭。
  • 切片時也可以用負數(shù)當參數(shù),規(guī)則和索引類似。
  • 切片可以和range函數(shù)一樣接受第三個參數(shù)作為步長,這個步長也可以是負數(shù),[::-1]這樣的索引方式可以方便地將列表倒轉。

在以上索引和切片的操作中還有一點要提的就是,如果索引的標簽超出列表范圍,程序就會拋出IndexError索引錯誤。

列表的加法和乘法

Python中的列表可以使用加法和乘法。那么為什么不能用減法和除法呢?因為索引和切片可以看作是使列表縮短的操作,這完成了減法和除法應該完成的任務。

Python列表的加法必須是兩個列表進行相加,效果是將兩個列表連接起來。

>>> sample1 = [1, '2', 'woo!']
>>> sample3 = [i for i in range(10) if i%2==0]
>>> sample1 + sample3
[1, '2', 'woo!', 0, 2, 4, 6, 8]

Python列表的乘法必須是列表乘以一個整數(shù),效果是將該列表重復。

>>> sample1 = [1, '2', 'woo!']
>>> sample1 * 2
[1, '2', 'woo!', 1, '2', 'woo!']

這兩個操作比較簡單,不多贅述了。自此我們掌握了一般的使列表長度變化的方法。接下來我們需要認識的是Python列表的一些簡單的其他內置方法。

列表的一些內置方法

這里涉及的都是一些簡單內置方法,包括insert,len,append,pop,reverse,sort, clear。

insert和len

insert方法能夠將新元素插入到列表的指定位置,接受兩個參數(shù),第一個是待插入的元素所插入位置的索引,第二個是該元素對象。

>>> sample3 = [i for i in range(10) if i%2==0]
>>> sample3
[0, 2, 4, 6, 8]
>>> sample3.insert(3, 1)
>>> sample3
[0, 2, 4, 1, 6, 8]

注意到插入時,將新元素插入要插入的索引位置后,從該位置起往后的列表元素都依次往后推了一格,索引加一。值得一提的是insert方法的第一個參數(shù)給定插入索引位置一樣可以是負數(shù),但是使用得不多。由于會把該位置原來的元素往后擠,所以即使是用-1當作參數(shù),新元素在操作結束后是列表的倒數(shù)第二個元素并不是倒數(shù)第一個。

len方法會返回列表的元素個數(shù),或者說叫列表的長度

>>> sample3 = [i for i in range(10) if i%2==0]
>>> len(sample3)
5
>>> sample3.insert(3, 1)
>>> len(sample3)
6

len方法堪稱是列表操作中最常用的方法,簡單實用。

在這里再介紹一個不是很常用但有時也有奇效的count方法,它接收一個參數(shù),返回該參數(shù)在列表中出現(xiàn)的次數(shù)。這里只是為了和len方法進行區(qū)分所以就不作演示了。

append和pop

append方法可以在列表尾端插入元素,彌補了insert函數(shù)的不足。而且遠遠不只是這樣,append操作的速度要比insert快上非常多。原因也是顯而易見的,append操作不會擠到列表中的元素所以不用更改其他元素的索引。對應的pop函數(shù)會刪除列表尾端的元素,并且返回該元素對象。

>>> sample3 = [i for i in range(10) if i%2==0]
>>> sample3
[0, 2, 4, 6, 8]
>>> sample3.append(1)
>>> sample3
[0, 2, 4, 6, 8, 1]
>>> sample3.pop()
1
>>> sample3
[0, 2, 4, 6, 8]

之所以要把這兩個方法放在一起講,是因為這兩個方法的存在使得Python列表天然地可以直接作為使用。棧是一種后進先出的緩存結構,在計算機中應用非常普遍。諸如遞歸、函數(shù)嵌套等,都是通過棧實現(xiàn)的。如果以后開了數(shù)據(jù)結構板塊我們會詳細介紹棧的強大。

reverse,sort和clear

reverse方法會將列表反轉。sort方法可以對列表元素進行遞增的排序,加入?yún)?shù)reverse=True則可以進行遞減的排序。clear方法會清空列表元素。

>>> sample3 = [i for i in range(10) if i%2==0]
>>> sample3
[0, 2, 4, 6, 8]
>>> sample3.reverse()
>>> sample3
[8, 6, 4, 2, 0]
>>> sample3.sort()
>>> sample3
[0, 2, 4, 6, 8]
>>> sample3.clear()
>>> sample3
[]

在以上涉及到改變列表長度的操作中, Python的處理辦法是容量不足時列表等量擴容,但是容量冗余時不會自動清除多余的容量。還是那句話,如果仔細討論Python數(shù)據(jù)結構的話,會對相關內容進行詳細說明。

列表是“可變”對象

介紹完列表的一些基本操作以后,我們要講一個比較重要的知識點。這是Python可變對象的一些特性,是面試時幾乎必然會涉及的內容。我們在此處先簡單介紹一下,在下一篇文章自定義函數(shù)相關內容中會更加仔細地介紹Python中可變對象的特性為什么會這么重要。

首先我們要明確的一點是,列表是可變對象。在上面我們介紹過的一系列操作,都是對列表對象本身進行操作。列表應該是我在本專題中第一次介紹可變對象的概念。我們沒接觸過可變對象沒關系,我們接觸過不可變對象。

>>> a = 3
>>> b = a
>>> a = a + 1
>>> print(b)
3

在上面的操作中我們對a賦值為3,再將a的值賦給b,再將a的值加1,這時我們輸出b的值,b還是3。這一切看上去是理所當然的。那我們試一下對列表進行這樣的操作呢?

>>> a = [3]
>>> b = a
>>> a = a + [1]
>>> print(a)
[3, 1]
>>> print(b)
[3, 1]

在上面的操作中我們對a賦值為單一元素的列表[3],再將a的值賦給b,再將列表a后面接上一個新列表[1]。根據(jù)剛才我們介紹過的列表操作可知,現(xiàn)在列表a應該變成了[3, 1]。試一下輸出a,確實是這樣的。但這時我們輸出列表b,b還會是[3]嗎?事實上不是的,現(xiàn)在列表b也變成了[3, 1]

這就是這個知識點重要的原因,我們沒有對列表b進行操作,可是它卻改變了。如果在實際項目中經(jīng)常出現(xiàn)這樣的現(xiàn)象,那恐怕bug多得永遠也清不完。這要求我們要時刻清楚地意識到Python對象的特性。這里先簡單說一下會發(fā)生這種現(xiàn)象的原因,具體的細節(jié)以及正確的操作方法在下一篇文章會重點介紹。

簡單來說就是對于可變對象,它的許多方法會修改這個對象本身,而對于不可變對象則是重新創(chuàng)建一個新對象。我們將值為3的a加1,事實上是將a指向了一個值等于3+1的新對象。而我們在列表[3]后面接上列表[1],它則是把列表[1]中的元素依次放進列表[3]的尾端。這兩種操作的區(qū)別是,前者創(chuàng)建了新對象而后者沒有,后者只是對原對象進行修改。因此如果a和b都是指向這個列表,那么這個列表變動時,a和b都會同時受到牽連。

這種特性要求我們分辨清楚,哪些操作是針對可變對象本身的,哪些操作是將可變對象作為參數(shù)參與運算而不進行修改的。如果我們不能合理地規(guī)避那些不應該出現(xiàn)的修改,那么將會有無盡的奇怪bug出現(xiàn)在我們的程序中。而分辨方法比較簡單,就和之前我們分辨函數(shù)變量和函數(shù)對象類似。如果列表對象出現(xiàn)在賦值語句的左邊,可以認為是對列表變量進行修改,如果在右邊則要分情況,如切片和索引就只訪問列表但不做修改,但是像resort方法pop方法就會修改列表了。

相信看到這里你大概能理解為什么我把標題取為“簡單數(shù)據(jù)結構”,單是列表,在絕大部分內容我們都是淺嘗輒止的情況下,已經(jīng)是非常大的篇幅了。我只能淺顯地介紹一下相關內容,更多內容在后面的學習過程中再繼續(xù)深入介紹。

字典 dictionary

字典顧名思義,就像字典一樣每個字對應一段解釋文字,Python中的字典也是非常典型的以逗號分隔以冒號分割的鍵值對數(shù)據(jù)結構,形式為:{鍵1:值1, 鍵2:值2, 鍵3:值3,}。和列表類似,字典的鍵和值對于賦值對象是比較包容的。而且注意到我們在字典的最后一個鍵值對后面還是加上了逗號。

這個逗號不是必須的,列表中我們不一定會這么做,但是在字典中我推薦這樣做。原因和字典的嵌套有關。

sample_dict = {key1:value1,
               key2:{value2_key1:value2_value1,
                     value2_key2:value2_value2,
                     value2_key3:{value2_value3_key1:value2_value3_value1,
                                  value2_value3_key2:value2_value3_value2,
                                  value2_value3_key3:value2_value3_value3,
                                  value2_value3_key4:value2_value3_value4,
                                 },
                     value2_key4:value2_value4,
                    },
               key3:value3,
               key4:value4,
               }

不要被上面的這個字典嚇到。事實上我們真正遇到的字典往往就是比這個還要復雜,但嵌套起來其實也很直觀,一目了然。我們以后處理json數(shù)據(jù)的話就會將其轉換為Python字典進行操作。注意到由于每個元素后面都有逗號,我們可以輕松地在某個元素后面回車來進行插入新的鍵值對的操作。也可以簡單地理解為,這樣的話字典的每個元素就變成了“鍵:值,”,從第一個元素到最后一個元素都是這樣,而我們在尾端按這種格式添加字段的時候,這種結構不會被破壞。

當然了,就和逗號后面加空格一樣,這種事情做不做一般不會影響程序的執(zhí)行,但是我們應該養(yǎng)成好習慣,提高代碼可讀性,減少出錯的機會。或者如果使用的字典很簡短,那么也可以不打最后這個逗號。

字典的鍵值對結構給其帶來了列表無法比擬的強大之處。它能完成信息檢索的功能,這是非常實用的功能,它可以輕松的建立映射結構。字典可以輕松地勝任數(shù)據(jù)結構,這對列表來說非常吃力。字典可以完成表驅動的一系列操作,使程序簡潔優(yōu)雅,思路清晰。另外,Python的字典是基于散列實現(xiàn),這使得它的檢索速度非常快。字典相關的深入的數(shù)據(jù)結構知識都是一些高級數(shù)據(jù)結構相關內容,這些知識都非常有趣,我會認真考慮是否有必要開數(shù)據(jù)結構專題的。

字典的索引

字典有類似于列表的索引訪問方式。字典[鍵]這種形式就可以訪問該鍵對應的值。

>>> a = {'b':'c', 'd':'e',}
>>> a['b']
'c'
>>> a.get('b')
'c'

我們這里順便介紹一下get方法,我們看到它和索引類似返回了字典鍵對應的值,那么它和索引有什么區(qū)別呢。

區(qū)別在于,我們如果用一個不存在的鍵去索引就會和引發(fā)和列表IndexError索引錯誤類似的KeyError鍵錯誤。但是get方法不會,如果在字典中找不到對應的鍵,則會返回一個None。那是不是get方法就一定比索引好用呢,也不是的。

因為字典的索引還可以用來賦值,修改字典或者添加鍵值對。當索引的鍵是字典中已經(jīng)存在時,賦值語句會修改該鍵對應的值,如果字典中并不存在這樣的鍵,則會新建一個鍵值對加入字典中。

字典遍歷

字典的遍歷主要分兩種,鍵和值的遍歷。首先字典本身是可迭代的,迭代的對象是字典中的鍵。

>>> a = {'b':'c', 'd':'e',}
>>> for i in a:
...     print(type(i), i)
...
<class 'str'> b
<class 'str'> d

這樣做有一種好處,我們得到了鍵,也就能用索引訪問它對應的值,這樣也就間接遍歷了字典中的值。說到底,字典的迭代的基本單位應該是鍵值對,但是Python把鍵作為鍵值對的代表,增加的操作的靈活性。

>>> a = {'b':'c', 'd':'e',}
>>> for i in a:
...     print(a[i])
...
c
e

除了直接遍歷,我們也可以使用字典的內置方法來直接得到我們需要的結果

>>> a = {'b':'c', 'd':'e',}
>>> a.keys()
dict_keys(['b', 'd'])
>>> a.values()
dict_values(['c', 'e'])
>>> a,items()
dict_items([('b', 'c'), ('d', 'e')])

需要說明的是這三個內置函數(shù)返回對象雖然不是列表,但都是可迭代對象,這意味著我們我們也可以用這些來遍歷字典。

>>> a = {'b':'c', 'd':'e',}
>>> for i in a.items():
...     print(type(i), i)
...
<class 'tuple'> ('b', 'c')
<class 'tuple'> ('d', 'e')

注意到items()方法的迭代對象是一個(鍵, 值)兩個元素組成的元組。元祖是我們接下來馬上要介紹的一種數(shù)據(jù)結構。而且其實我們之前用的直接對字典遍歷for i in a其實就相當于for i in a.keys()

字典的一些內置方法

其實在上面我們已經(jīng)接觸了一些字典的內置方法,他們都是一些訪問字典內容的方法,這里我們要講的主要是poppopitem這兩個從字典中彈出元素的方法。

我們在介紹列表時已經(jīng)接觸過pop方法了,但是字典的pop方法不一樣,字典的pop方法需要指定一個鍵作為參數(shù),返回值為該鍵在字典中對應的值。

>>> a = {'b':'c', 'd':'e',}
>>> a.pop('b')
'c'
>>> a
{'d': 'e'}

除了用pop方法外,也可以用諸如del a['b']這樣的形式用內置方法從字典中刪除指定鍵值對,但是不同的是不會有返回值。事實上支持索引的可變數(shù)據(jù)類型基本上都支持del方法。

那么這個popitem方法又是用來干什么的呢。其實字典的popitem方法倒更像列表的pop方法,注意只是“像”,這兩者還是有區(qū)別的。

>>> a = {'b':'c', 'd':'e',}
>>> a.popitem()
('d', 'e')
>>> a
{'b': 'c'}

注意到字典popitem方法和列表pop方法一樣不需要參數(shù),會彈出一個元素作為返回值。這個返回值的形式和items方法的迭代對象類似,是個兩個元素的元組。但這不是我要說的區(qū)別,我要說的區(qū)別是,Python列表是順序表,所以它能彈出最后一個元素,但是Python字典的鍵值對的順序其實是不可靠的,不要寄希望于popitem方法來彈出最后插入的鍵值對,可以認為popitem方法是從字典中隨機彈出一個鍵值對。

要想按插入順序后入先出彈出字典元素可以使用collections中的OrderedDict,文末會與其他標準庫補充數(shù)據(jù)類型一起進行更多討論。

由于有了介紹時列表建立的一部分基礎,字典理解起來和列表進行對比記憶就行了。還是那句話,更深入的應用會在可能出現(xiàn)的數(shù)據(jù)結構部分講解,本文目前更需要關注的是理解這些數(shù)據(jù)結構的特點與簡單的使用方法,和在實際操作中去熟悉這些數(shù)據(jù)結構。

元組 tuple 和 集合 set

元組tuple和集合set是Python中的另外兩個常用數(shù)據(jù)結構。我們在上文中強調過列表list是可變對象,可以把元組tuple簡單理解為不可變的列表。這意味著列表支持的許多操作元組是不支持的,但這同時也使得元組比列表安全許多。因此,在Python許多內置操作和函數(shù)中,元組的應用反而要比列表更加廣泛。

我們說可以把元組tuple簡單理解為不可變的列表,類似的,集合set可以簡單理解為不含重復元素的列表。

創(chuàng)建元組

元組創(chuàng)建方式和列表類似,但是符號改成圓括號。

>>> a = ('b', 'c', 'd')
>>>type(a)
<class 'tuple'>
>>> b = 'c', 'd', 'e'
>>> type(b)
<class 'tuple'>
>>> print(b)
('c', 'd', 'e')

注意到我們還使用了一種直接定義方式, 當我們在賦值語句的等號右邊用逗號分隔多個對象時,如果等號左邊只有一個變量,那么這些對象將會作為元組的元素賦值給該變量。和字典一樣,元組的逗號也有一點規(guī)定,單個元素的元組,元素后面必須加上逗號

在這里我們要把定義列表的方法再拿出來說一說,我們是不是可以認為列表的一般賦值語句sample_list = [elem1, elem2]是相當于對元組對象(elem1, elem2)[]的形式使用了Python內置的list函數(shù)將其轉換成列表呢?

是也不是。Python的列表生成操作中[]或者list(iter)其實是用于任何可迭代對象的函數(shù)。而我們見到的b = 'c', 'd', 'e'賦值直接把其作為元組保存是因為,Python中元組比列表是更優(yōu)先的元素存儲數(shù)據(jù)結構。這也是我說Python中元組可能比列表反而應用更廣泛的原因,元組是Python中默認的最基本可迭代對象形式。關于賦值語句右端用逗號分隔的對象是可迭代對象,我們會在下一篇函數(shù)參數(shù)相關知識中進行講解。

而元組比列表更被Python推崇的原因恰好就在于,元組是不可變的對象。這使它被認為是更“安全”的。這也是我們在上文中花大量篇幅說列表是可變對象的原因之一,元組與列表最大的區(qū)別就在此。

元組的基本操作

元組可以像列表一樣索引和切片,可以用加法和乘法,可以用len獲取長度。但是列表那些改變自己本身的操作元組是不支持的,比如說pop和append或者像sort和reverse方法。即使元組也是有序的,但它不能做這些事情。

但是元組的元素排序不是做不到的,我們用sorted(iter)這個內置函數(shù)就可以了。這個函數(shù)就像list(iter)函數(shù)一樣可以對任何可迭代對象使用,但是返回結果是列表。我們可以對返回結果使用tuple()函數(shù)再賦值給某個變量就可以了。當然,你也看出來了,這有點蛋疼,還是直接用列表吧。

那么什么時候該用列表,什么時候該用元組呢。我的建議是,當元素類型單一結構相似而變化范圍較大的時候使用列表,這樣可以方便我們進行操作。而元素類型復雜或者元素類型單一但是元素相對確定的時候,我們可以使用元組作為變量保存池。舉例來說星期一到星期天這七天更適合存儲在元組中,而我們的待辦事項更適合存儲在列表中。當然,如果要將星期和待辦事項進行對應,我們應該使用字典。

說到字典。我們上文中提到字典對存儲對象比較寬容,而討論列表時我們用的卻是非常寬容?,F(xiàn)在我們看看為什么字典不那么寬容。我們提到過Python字典某種程度上其實可以看作是哈希表,這要求Python字典的鍵是常量,也就是我強調過的不可變對象,因此,元組可以作為字典中的鍵,但是列表不可以。

在下一篇函數(shù)中,我們還會遇到元組和列表對比相關的問題。

集合

我并不打算對集合展開太多的內容。就像之前所提到過的,我們只要將其理解成不包含重復元素的列表即可。不過需要注意的是集合中的元素是無序的,因此集合不支持索引。在這里只簡單介紹一下集合的定義方式。

>>> a = {'b', 'c', 'd', 'd'}
>>> print(type(a), a)
<class 'set'> {'c', 'd', 'b'}
>>> a = {}
>>> type(a)
<class 'dict'>

注意到集合在定義時會自動刪除重復的元素。集合的符號和字典類似,但是我們定義空集合時不能使用{},這樣得到的是一個空字典。我們可以用定義集合的另一種方式定義空集合,Python中對可迭代對象可以像列表和元組一樣使用set()函數(shù)來生成一個新集合。

>>> a = set()
>>> print(type(a), a)
<class 'set'> set()
>>> a.add('b', 'c')
>>> a
{'b', 'c'}
>>> a.add('c', 'd')
>>> a
{'b', 'c', 'd'}

注意到我們使用add方法向集合中添加了元素,這說明集合也是可變對象。而且不管我們使用什么樣的方法,集合中永遠不會出現(xiàn)重復的元素。

另外值得一提的是集合之間的操作,即數(shù)學意義上的交、并、補、異或,它們對應的操作符分別是&, |, -, ^

Python其他內置數(shù)據(jù)結構

在Python的標準庫collections中還有許多適應不同需求的其他數(shù)據(jù)結構,常見的比如deque用來比列表更好地完成堆棧隊列的功能,Counter用來以字典的形式保存列表中元素出現(xiàn)的次數(shù),OrderDict用來構造有序的字典,defaultdict用來構造有默認值的字典,以及其他一系列的用來構造自定義復雜數(shù)據(jù)結構的基礎數(shù)據(jù)類型類。

這些數(shù)據(jù)結構應用比較靈活,在以后的實際操作中偶爾會接觸到他們。在這里我也不打算展開來介紹了。

最后的廢話

講到這里我想要講的有關Python簡單數(shù)據(jù)結構內容就基本結束了。我們把主要精力放在了列表上。我們通過列表熟悉了數(shù)據(jù)結構是用來做什么的,大概會具有什么樣的功能,因為列表是功能比較齊全,比較典型而且應用廣泛的數(shù)據(jù)結構。這樣我們理解元組和集合比較簡單,一個可以簡單理解為不可變列表,一個可以理解為不含重復元素的無序列表。另一個我們介紹比較詳細的是字典,這種數(shù)據(jù)格式是互聯(lián)網(wǎng)信息交互中應用最廣泛的類似數(shù)據(jù)形式。

最后再說明一下為什么我會猶豫要不要詳細講Python中的復雜數(shù)據(jù)結構,開個專門的板塊來介紹。畢竟在我計劃中字符串都是有單獨板塊的。

這是因為我認為Python中數(shù)據(jù)結構沒那么重要。我覺得Python的學習非常簡單,一個是面向對象的理念要落實,一個是放開思路靈活運用第三方庫。前一個幫助我們理解別人的代碼到底是什么意思,后者能夠幫助我們快速地解決問題。一切編程方法,無論是面向這個面向那個,基于這個基于那個,說到底是基于計算機面向問題編程。能解決問題才是關鍵

因此,如果有數(shù)據(jù)結構基礎,那么完全沒有必要學習如何用Python構造樹和圖或者哈希表。簡單的就簡單用deque完成棧隊功能或者使用字典即可,復雜的可能說你思路需要修改或者說根本不適合用Python來完成復雜數(shù)據(jù)結構。

這樣說可能顯得我根本不需要猶豫,直接略過數(shù)據(jù)結構就可以了。但是不是這樣的。我覺得任何想要深入編程技術的人,都應該學習數(shù)據(jù)結構,應該看一下優(yōu)秀的前輩是如何解決問題的。為什么他們可以想出這樣的數(shù)據(jù)結構完成這樣的功能。再一點,用Python來學習數(shù)據(jù)結構,剛好就是熟悉Python面向對象的最好方式。我們也可以通過學習比較看出Python面向對象時與其他語言的異同點。

在這里先在文末給大家推薦兩個通過學習數(shù)據(jù)結構的途徑。一個是在線的英文網(wǎng)站,文章內容很高,而且支持實時操作測試,如果沒有語言障礙會是非常好的學習體驗,強烈推薦。另一個是我看過的一本實體書,北大老師寫的《數(shù)據(jù)結構與算法:Python語言描述》。我直言不諱地講里面的代碼質量不高,很多地方值得改進,對Python特點理解得也不算透徹,很多代碼可以明顯看出C的痕跡。但是這本書相對簡單,很扎實,講解也比較詳盡,對于初學者來說也算不錯的學習材料。如果要學習系統(tǒng)的數(shù)據(jù)結構與算法的知識,coursera上有大量的斯坦福和普林斯頓等國外高校的優(yōu)質課程,這里就不貼鏈接了,會科學上網(wǎng)不至于不知道coursera。

鏈接

  1. Problem Solving with Algorithms and Data Structures using Python — Problem Solving with Algorithms and Data Structures
  2. 數(shù)據(jù)結構與算法:Python語言描述 (豆瓣)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容