【Chapter 3】 Python的數(shù)據(jù)結(jié)構(gòu)拾枣、函數(shù)和文件
本章討論 Python 的內(nèi)置功能奈揍,我們會(huì)從Python最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)開始:元組、列表紊册、字典和集合比肄。然后會(huì)討論創(chuàng)建你自己的快耿、可重復(fù)使用的Python函數(shù)。最后芳绩,會(huì)學(xué)習(xí)Python的文件對(duì)象掀亥,以及如何與本地硬盤交互。
3.1 數(shù)據(jù)結(jié)構(gòu)和序列
元組(Tuple)
元組是長(zhǎng)度固定妥色,不可改變的序列(可以看做長(zhǎng)度固定搪花,且不可變的列表)。創(chuàng)建元祖的方法是用逗號(hào):
1. 合并元組
用 “+” 合并元組嘹害。
In [11]: (4,5,6)+(7,8)
Out[11]: (4, 5, 6, 7, 8)
2. 取出元組
In [14]: tup = 4, 5, (6, 7)
...: a, b, (c, d) = tup
...:
In [15]: d
Out[15]: 7
交換變量名
In [19]: a
Out[19]: 4
In [20]: b
Out[20]: 5
In [21]: b,a = a,b
In [22]: a
Out[22]: 5
In [23]: b
Out[23]: 4
類似上例的 unpacking 方法常用來迭代元組或列表序列:
In [35]: for a,b,c in seq:
...: print('a={0},b={1},c={2}'.format(a,b,c))
...:
a=1,b=2,c=3
a=4,b=5,c=6
a=7,b=8,c=9
另一種更高級(jí)的 unpacking 方法是用于只取出tuple中開頭幾個(gè)元素撮竿,剩下的元素直接賦給*rest
:
In [36]: values = 1,2,3,4,5
In [37]: a,b,*rest = values
In [38]: a,b
Out[38]: (1, 2)
In [39]: rest
Out[39]: [3, 4, 5]
rest 部分是你想要丟棄的,名字本身無所謂笔呀,通常用下劃線來代替:
In [40]: a, b, *_ = values
In [41]: _
Out[41]: [3, 4, 5]
3. Tuple methods(元組方法)
因?yàn)閠uple的大小和內(nèi)容都不能改變幢踏,所以方法也很少。count
用來計(jì)算某個(gè)值出現(xiàn)的次數(shù)许师,list中也有這個(gè)方法:
In [42]: a = (1, 2, 2, 2, 3, 4, 2)
...: a.count(2)
...:
Out[42]: 4
List(列表)
列表的靈活性就很強(qiáng)了房蝉,大小和內(nèi)容都可以變。
list 函數(shù)通常用來具現(xiàn)化迭代器或生成器:
In [43]: gen =range(10)
In [44]: lis(gen)
In [45]: list(gen)
Out[45]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1. 添加
list.append()
在列表末尾添加;
list.insert(index,element)
可以在特定的位置插入元素 ;
警告:與
append
相比微渠,insert
耗費(fèi)的計(jì)算量大搭幻,因?yàn)閷?duì)后續(xù)元素的引用必須在內(nèi)部遷移,以便為新元素提供空間逞盆。如果要在序列的頭部和尾部插入元素檀蹋,你可能需要使用collections.deque
,一個(gè)雙尾部隊(duì)列云芦。
2.刪除
insert的逆運(yùn)算是pop(index)
,它移除并返回指定位置的元素;
可以用remove
去除某個(gè)值纱昧,remove('element')
會(huì)先尋找第一個(gè)值并除去;
如果不考慮性能善已,使用append
和remove
,可以把Python的列表當(dāng)做完美的“多重集”數(shù)據(jù)結(jié)構(gòu)耀盗。
3.包含
用in
可以檢查列表是否包含某個(gè)值:
In [55]: 'dwarf' in b_list
Out[55]: True
4. 串聯(lián)和組合列表
與元組類似,可以用加號(hào)將兩個(gè)列表串聯(lián)起來:
In [57]: [4, None, 'foo'] + [7, 8, (2, 3)]
Out[57]: [4, None, 'foo', 7, 8, (2, 3)]
如果已經(jīng)定義了一個(gè)列表,用extend
方法可以追加多個(gè)元素:
In [58]: x = [4, None, 'foo']
In [59]: x.extend([7, 8, (2, 3)])
In [60]: x
Out[60]: [4, None, 'foo', 7, 8, (2, 3)]
通過加法將列表串聯(lián)的計(jì)算量較大扫尺,因?yàn)橐陆ㄒ粋€(gè)列表婿崭,并且要復(fù)制對(duì)象。
用extend
追加元素,尤其是到一個(gè)大列表中徒欣,更為可取粗梭。因此:
everything = []
for chunk in list_of_lists:
everything.extend(chunk)
要比串聯(lián)方法快:
everything = []
for chunk in list_of_lists:
everything = everything + chunk
總結(jié):
append 和 extend 的區(qū)別。
- append 是把元素添加到一個(gè)list里
- extend 是把兩個(gè) list (多個(gè)元素)結(jié)合在一起
extend 和 + 的區(qū)別
+
是創(chuàng)建了一個(gè)新的list并返回断医,運(yùn)算量大- extend 是在原本的list上做了更改滞乙,運(yùn)算量小
5. 排序
你可以用sort
函數(shù)將一個(gè)列表原地排序(不創(chuàng)建新的對(duì)象):
In [61]: a = [7, 2, 5, 1, 3]
In [62]: a.sort()
In [63]: a
Out[63]: [1, 2, 3, 5, 7]
sort
有一些選項(xiàng)奏纪,有時(shí)會(huì)很好用。其中之一是二級(jí)排序key斩启,可以用這個(gè)key進(jìn)行排序亥贸。例如,我們可以按長(zhǎng)度對(duì)字符串進(jìn)行排序:
In [64]: b = ['saw', 'small', 'He', 'foxes', 'six']
In [65]: b.sort(key=len)
In [66]: b
Out[66]: ['He', 'saw', 'six', 'small', 'foxes']
二分搜索和維持一個(gè)排好序的 list 列表
bisect
模塊支持二分查找浇垦,和向已排序的列表插入值炕置。:
bisect.bisect
其目的在于查找該數(shù)值將會(huì)插入的位置并返回,而不會(huì)插入男韧。
bisect.insort
是插入一個(gè)值朴摊,而插入后的列表還是整齊的。
In [47]: c= [1,2,2,2,3,4,7]
In [48]: bisect.bisect(c,2)
Out[48]: 4
In [49]: bisect.bisect(c,5)
Out[49]: 6
In [50]: bisect.bisect(c,6)
Out[50]: 6
In [51]: c
Out[51]: [1, 2, 2, 2, 3, 4, 7]
注意:bisect模塊不會(huì)檢查list是否是排好序的此虑,對(duì)未排序的列表使用bisect
不會(huì)產(chǎn)生錯(cuò)誤甚纲,但結(jié)果不一定正確 ,所以用這個(gè)模塊之前要先把list排序朦前。
Slicing (切片)
[start:stop], 輸出的結(jié)果包含開頭介杆,不包含結(jié)尾。所以輸出的結(jié)果的數(shù)量是stop-start 韭寸。即區(qū)間為[start春哨,stop)。
In [73]: seq = [7, 2, 3, 7, 5, 6, 0, 1]
In [74]: seq[1:5]
Out[74]: [2, 3, 7, 5]
start
或 stop
都可以被省略恩伺,省略之后赴背,分別默認(rèn)序列的開頭和結(jié)尾(負(fù)索引表示倒數(shù)開始多少個(gè)的意思):
In [77]: seq[:5]
Out[77]: [7, 2, 3, 6, 3]
In [78]: seq[-4:]
Out[78]: [5, 6, 0, 1]
兩個(gè)冒號(hào)后面的數(shù)代表步長(zhǎng)(step),就是隔(step-1)個(gè)元素取一次:
In [80]:seq
Out[80]:[7, 2, 3, 6, 3, 5, 6, 0, 1]
In [81]: seq[::2]
Out[81]: [7, 3, 3, 6, 1]
序列函數(shù)
1. enumerate(枚舉)函數(shù)
迭代一個(gè)序列時(shí)晶渠,你可能想跟蹤當(dāng)前項(xiàng)的序號(hào) 凰荚。一個(gè)比較直白的方法是:
i = 0
for value in collection:
# do something with value
i += 1
但 Python 內(nèi)建了一個(gè)enumerate
函數(shù),可以返回(i, value)
元組序列:
for i, value in enumerate(collection):
enumerate 通常用來把一個(gè) list 中的位置和值映射到一個(gè) dcit 字典里:
In [57]: some_list = ['foo','bar','baz']
In [58]: mapping={}
In [59]: for i,v in enumerate(some_list):
...: mapping[v] = i
...:
In [60]: mapping
Out[60]: {'bar': 1, 'baz': 2, 'foo': 0}
2. sorted()
sorted
函數(shù)可以從任意序列的元素返回一個(gè)新的排好序的列表:
In [71]: sorted([1,23,4,5,2,7])
Out[71]: [1, 2, 4, 5, 7, 23]
In [72]: sorted('jadon sunshine')
Out[72]: [' ', 'a', 'd', 'e', 'h', 'i', 'j', 'n', 'n', 'n', 'o', 's', 's', 'u']
sorted
函數(shù)可以接受和sort
相同的參數(shù)褒脯。
3. zip 函數(shù)
用于"pairs"(成對(duì))便瑟。把多個(gè)列表、元組或其它序列每個(gè)對(duì)應(yīng)的元素變成一對(duì)番川,最后返回一個(gè)含有 tuple 的 list:
In [73]: seq1 = ['foo', 'bar', 'baz']
In [74]: seq2 = ['one', 'two', 'three']
In [75]: zipped = zip(seq1,seq2)
In [76]: list(zipped)
Out[76]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
zip 可以接收任意長(zhǎng)度的序列到涂,最后返回的結(jié)果取決于最短的序列:
In [77]: seq3 = [False, True]
...: list(zip(seq1, seq2, seq3))
...:
Out[77]: [('foo', 'one', False), ('bar', 'two', True)]
zip 的一個(gè)常見用法是同時(shí)迭代多個(gè)序列,可以和 enumerate 搭配起來用:
In [79]: for i,(a,b) in enumerate(zip(seq1,seq2)):
...: print('{0}:{1},{2}'.format(i,a,b))
...:
0:foo,one
1:bar,two
2:baz,three
如果給我們一個(gè)壓縮過的序列爽彤,我們可以將其解壓:
In [81]: pitchers = [('Nolan', 'Ryan'), ('Roger', 'Clemens'),
...: ('Schilling', 'Curt')]
...:
In [82]: first_names,last_names = zip(*pitchers) #解壓
In [84]: first_names
Out[84]: ('Nolan', 'Roger', 'Schilling')
In [85]: last_names
Out[85]: ('Ryan', 'Clemens', 'Curt')
5.reversed
reverse可以倒敘迭代序列:
In [86]: list(reversed(range(10)))
Out[86]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
注意养盗,revered是一個(gè)generator(生成器,之后會(huì)詳細(xì)講)适篙,所以必須需要(list 或 for循環(huán))來具現(xiàn)化 往核。
字典
1. 字典創(chuàng)建、訪問嚷节、插入聂儒、更改
字典也被叫做hash map 或 associative array虎锚。結(jié)構(gòu)就是key-value pairs.創(chuàng)建方式是用{key : value}
:
In [101]: empty_dict = {}
In [102]: d1 = {'a' : 'some value', 'b' : [1, 2, 3, 4]}
In [103]: d1
Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}
你可以像訪問列表或元組中的元素一樣,訪問衩婚、插入或設(shè)定字典中的元素:
In [87]: d1 = {'a': 'some value', 'b': [1, 2, 3, 4]}
In [88]: d1[7] ='an integer'#插入
In [90]: d1
Out[90]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
In [91]: d1['b']#訪問
Out[91]: [1, 2, 3, 4]
你可以用檢查列表和元組是否包含某個(gè)值得方法窜护,檢查字典中是否包含某個(gè)鍵:
In [93]: 'b' in d1
Out[93]: True
可以用del
關(guān)鍵字或pop
方法(返回值得同時(shí)刪除鍵)刪除值:
In [94]: d1[5] = 'some value'
In [95]: d1
Out[95]: {5: 'some value', 7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
In [96]: del d1[5]
In [97]: d1
Out[97]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
In [101]: d1
Out[101]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
In [102]: ret = d1.pop(7)
In [103]: d1
Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}
keys 和 values 方法能返回dict中key-value組合的迭代器,不過并不按什么順序非春。如果想讓 keys 和 values 每次打印的順序相同的話:
In [106]: d1
Out[106]: {7: 'integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
In [108]: list(d1.keys())
Out[108]: ['a', 'b', 7]
In [110]: list(d1.values())
Out[110]: ['some value', [1, 2, 3, 4], 'integer']
用update
方法可以將一個(gè)字典與另一個(gè)融合:
In [119]: d1.update({'b' : 'foo', 'c' : 12})
In [120]: d1
Out[120]: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
update
方法是原地改變字典柱徙,因此任何傳遞給update
的鍵的舊的值都會(huì)被舍棄。
2.用序列創(chuàng)建字典
常常奇昙,你可能想將兩個(gè)序列配對(duì)組合成字典护侮。下面是一種寫法:
mapping = {}
for key, value in zip(key_list, value_list):
mapping[key] = value
因?yàn)樽值浔举|(zhì)上是 2元元組 的集合,dict可以接受 2元元組的列表:
In [121]: mapping = dict(zip(range(5), reversed(range(5))))
In [122]: mapping
Out[122]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
3. Default value(默認(rèn)值)
如果 dict 中某個(gè)key存在的話储耐,就返回該value羊初,否則的話,就返回一個(gè)默認(rèn)值:
if key in some_dict:
value = some_dict[key]
else:
value = default_value
不過dict中的get 和pop方法能設(shè)置默認(rèn)值什湘,即能把上面的代碼簡(jiǎn)寫為:
value = some_dict.get(key, default_value)
如果key不存在的話长赞,get方法默認(rèn)會(huì)返回None,而pop則會(huì)引發(fā)一個(gè)錯(cuò)誤闽撤。
通過設(shè)定值得哆,一個(gè)常用的場(chǎng)景是一個(gè)dict中的value也是其他集合,比如list腹尖。舉例說明柳恐,我們想要把一些單詞按首字母歸類:
In [128]: words = ['apple', 'bat', 'bar', 'atom', 'book']
...: by_letter = {}
...:
In [129]: for word in words:
...: letter = word[0]
...: if letter not in by_letter:
...: by_letter[letter] = [word]#新建一個(gè) key:value
...: else :
...: by_letter[letter].append(word)#如果已經(jīng)存在這個(gè) key 直接添加 value
...:
In [129]:
In [130]: by_letter
Out[130]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
而 setdefault 方法則是專門為這個(gè)用途存在的,上面的循環(huán)可以寫為:
for word in words:
letter = word[0]
by_letter.setdefault(letter, []).append(word)
使用setdefault() 初始化字典鍵值. 使用字典的時(shí)候經(jīng)常會(huì)遇到這樣一種應(yīng)用場(chǎng)景:動(dòng)態(tài)更新字典热幔,像如上面代碼,如果 key 不在 dictionary 中那么就添加它并把它對(duì)應(yīng)的值初始為空列表[] 讼庇,然后把元素append到空列表中绎巨。
內(nèi)建的collections模塊有一個(gè)有用的class,defaultdict蠕啄,這個(gè)能讓上述過程更簡(jiǎn)單场勤。創(chuàng)建方法是傳遞一個(gè)type或是函數(shù):
from collections import defaultdict
by_letter = defaultdict(list)
for word in words:
by_letter[word[0]].append(word)
Valid dict key types(有效的key類型)
通常key的類型是不可更改的常量類型(int,float歼跟,string)或tuple和媳。專業(yè)的叫法是hashability」郑可以查看一個(gè)object是否是hashable留瞳,只要是hashable的,就可以當(dāng)做dict中的key骚秦。這里用hash函數(shù)查看:
In [127]: hash('string')
Out[127]: 5023931463650008331
In [128]: hash((1, 2, (2, 3)))
Out[128]: 1097636502276347782
In [129]: hash((1, 2, [2, 3])) # fails because lists are mutable
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-129-800cd14ba8be> in <module>()
----> 1 hash((1, 2, [2, 3])) # fails because lists are mutable
TypeError: unhashable type: 'list'
要用列表當(dāng)做鍵她倘,一種方法是將列表轉(zhuǎn)化為元組璧微,只要內(nèi)部元素可以被哈希,它也就可以被哈希:
In [131]: d = {}
In [132]: d[tuple([1,2,3])]=5
In [133]: d
Out[133]: {(1, 2, 3): 5}
Set 集合
集合是無序的不可重復(fù)的元素的集合硬梁。你可以把它當(dāng)做字典前硫,但是只有鍵沒有值∮梗可以用兩種方式創(chuàng)建集合:通過 se t函數(shù)或使用尖括號(hào) set 語句:
In [133]: set([2, 2, 2, 1, 3, 3])
Out[133]: {1, 2, 3}
In [134]: {2, 2, 2, 1, 3, 3}
Out[134]: {1, 2, 3}
集合支持合并屹电、交集、差分和對(duì)稱差等數(shù)學(xué)集合運(yùn)算跃巡∴拖辏考慮兩個(gè)示例集合:
In [135]: a = {1, 2, 3, 4, 5}
In [136]: b = {3, 4, 5, 6, 7, 8}
合并是取兩個(gè)集合中不重復(fù)的元素〈膳冢可以用union
方法葱色,或者|
運(yùn)算符:
In [136]: # 并集
...: a.union(b)
Out[136]: {1, 2, 3, 4, 5, 6, 7, 8}
In [137]: a|b
Out[137]: {1, 2, 3, 4, 5, 6, 7, 8}
交集的元素包含在兩個(gè)集合中∧锵悖可以用intersection
或&
運(yùn)算符:
In [138]: # 交集
...: a.intersection(b)
Out[138]: {3, 4, 5}
In [139]: a & b
Out[139]: {3, 4, 5}
所有邏輯集合操作都有另外原地實(shí)現(xiàn)方法苍狰,它可以直接用結(jié)果替代集合的內(nèi)容。對(duì)于大的集合烘绽,這么做效率更高:
In [141]: c = a.copy()
In [142]: c |= b
In [143]: c
Out[143]: {1, 2, 3, 4, 5, 6, 7, 8}
In [144]: d = a.copy()
In [145]: d &= b
In [146]: d
Out[146]: {3, 4, 5}
set 的元素必須是不可更改的淋昭。如果想要 list 一樣的元素,只能變?yōu)閠uple:
In [157]: my_data = [1,2,3,4]
In [158]: my_set = {tuple(my_data)}
In [159]: my_set
Out[159]: {(1, 2, 3, 4)}
你還可以檢測(cè)一個(gè)集合是否是另一個(gè)集合的子集或父集:
In [150]: a_set = {1, 2, 3, 4, 5}
In [151]: {1, 2, 3}.issubset(a_set)
Out[151]: True
In [152]: a_set.issuperset({1, 2, 3})
Out[152]: True
集合的內(nèi)容相同時(shí)安接,集合才對(duì)等:
In [153]: {1, 2, 3} == {3, 2, 1}
Out[153]: True
列表翔忽、集合和字典推導(dǎo)式
list comprehension(列表推導(dǎo)式)是python里最受喜愛的一個(gè)特色。我們能簡(jiǎn)潔地構(gòu)造一個(gè)list:
[expr for val in collection if condiction]
相當(dāng)于:
result = []
for val in collection:
if condition:
result.append(expr)
比如盏檐,給定一個(gè)list歇式,里面有很多string,我們只要留下string長(zhǎng)度超過2的胡野,并將其轉(zhuǎn)換為大寫:
In [160]: strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
In [161]: [x.upper() for x in strings if len(x) > 2]
Out[161]: ['BAT', 'CAR', 'DOVE', 'PYTHON']
dict推導(dǎo)式:
dict_comp = {key-expr: value-expr for value in collection if condition}
set的推導(dǎo)式:(集合的推導(dǎo)式與列表很像材失,只不過用的是尖括號(hào): )
set_comp = {expr for value in collection if condition}
基于上面的例子,我們想要一個(gè)集合來保存string的長(zhǎng)度:
map
函數(shù)可以進(jìn)一步簡(jiǎn)化:
In [158]: set(map(len, strings))
Out[158]: {1, 2, 3, 4, 6}
下面一個(gè)簡(jiǎn)單的字典表達(dá)式例子硫豆,查找 字符串和其在list中對(duì)應(yīng)的index:
In [159]: loc_mapping = {val : index for index, val in enumerate(strings)}
In [160]: loc_mapping
Out[160]: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
作者:SeanCheney
鏈接:http://www.reibang.com/p/b444cda10aa0
來源:簡(jiǎn)書
著作權(quán)歸作者所有龙巨。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處熊响。
Nested list comprehensions(嵌套列表表達(dá)式)
假設(shè)我們有一個(gè)list旨别,list中又有不同的list表示英語和西班牙語的姓名:
all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'],
['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]
你可能是從一些文件得到的這些名字,然后想按照語言進(jìn)行分類『骨眩現(xiàn)在假設(shè)我們想用一個(gè)列表包含所有的名字秸弛,這些名字中包含兩個(gè)或更多的e。可以用for循環(huán)來做:
names_of_interest = []
for names in all_data:
enough_es = [name for name in names if name.count('e') >= 2]
names_of_interest.extend(enough_es)
可以用嵌套列表推導(dǎo)式的方法胆屿,將這些寫在一起奥喻,如下所示:
In [169]: result = [name for names in all_data for name in names if name.count('e')>=2]
In [170]: result
Out[170]: ['Steven']
for部分是根據(jù)嵌套的順序來寫的,從外層的loop到內(nèi)層的loop非迹。這里一個(gè)例子是把tuple扁平化成一個(gè)整數(shù)列表:
In [171]: some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
...: flattened = [x for tup in some_tuples for x in tup]
...: flattened
...:
Out[171]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
一定要記住順序是和我們寫for loop一樣的:
In [172]: flatteded = []
...:
...: for tup in some_tuples:
...: for x in tup:
...: flattened.append(x)
列表表達(dá)式里再有一個(gè)列表表達(dá)式也是可以的环鲤,可以生成a list of lists:
In [173]: [[x for x in tup] for tup in some_tuples]
Out[173]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]