《Python官方文件》5.資料結構
資料結構
本章會更細節的講一些你已經學過的東西,同時也會加一些新的內容。
5.1 List有關方法
list有許多方法,以下是list方法的所有例項: list.append(x)
新增1個item到list的末尾,等同於a[len(a):] = [x]
list.extend(iterable)
以迭代器方式追加的所有item拓展list,等同於a[len(a):] = iterable
list.insert(x)
把x插入到list的指定位置。第一個引數是插入的位置,a.insert(0, x)
插入到list頭, a.insert(len(a), x)
相當於a.append(x)
。 list.remove(x)
list.pop([i])
移除list中指定位置的item,同時返回item。如果位置沒有指定,a.pop()'會移除list的最後1個item,並返回它。中括號中的引數x是可選的,中括號不是必須的。你在Python手冊中會經常看見這種用法。
list.clear() 清空list,等同於
del a[:]list.index(x[, start[, end]]) 返回第一個等於x的item的序列位置,是從0開始算。如果沒有等於x的item方法會報
ValueError。 可選引數作為切片使用,定義了起始和結束位置。返回的位置是相對於原先完整的list而不是起始引數。
返回list中x出現的次數
list.sort(key=None, reverse=False) 本地對list排序(引數是拿來定義sort,去sorted()看具體的解釋吧)
list.reverse() 不需要額外空間,倒排list。
list.copy() 返回list的淺拷貝。相當於
a[:]`
以下例項使用了上述大部分方法: fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana'] fruits.count('apple') 2 fruits.count('tangerine') 0 fruits.index('banana') 3 fruits.index('banana', 4) # Find next banana starting a position 4 6 fruits.reverse() fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange'] fruits.append('grape') fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape'] fruits.sort() fruits ['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear'] fruits.pop() 'pear'
5.1.1 像棧一樣使用list
list這些方法使得list非常容易像棧一樣被使用,最後1個插入的元素首先被取出,新增到棧頂使用append();獲取棧頂元素使用pop(),不需要特別指定次序:
fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
fruits.count('apple')
2
fruits.count('tangerine')
0
fruits.index('banana')
3
fruits.index('banana', 4) # Find next banana starting a position 4
6
fruits.reverse()
fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
fruits.append('grape')
fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
fruits.sort()
fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
fruits.pop()
'pear'
5.1.2 像佇列一樣使用list
當然也可以把list作為佇列,先進先出,但是,作為佇列list並不高效。因為從list尾部追加刪除原色很快,但是從list頭做同樣操作就很慢了,因為其他所有元素都要移動1次。 真要實現佇列,請使用collections.deque
這個專門的資料結構,它2端插入提取都很快:
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry") # Terry arrives
queue.append("Graham") # Graham arrives
queue.popleft() # The first to arrive now leaves
'Eric'
queue.popleft() # The second to arrive now leaves
'John'
queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
5.1.3 List 列表解析式
list 的列表解析式提供了一種簡潔的建立list的方法。在以下場景我們常使用list列表解析式:生成1個新的list,它的元素是有原始序列資料結構中取出每個元素操作;或者產生原始序列結構的子集。 比如說你想建立原始list的平方list:
squares = []
for x in range(10):
... squares.append(x**2)
...
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
注意到以上程式碼會建立一個變數x,它在迴圈後仍就存在。為了消除這個副作用:
squares = list(map(lambda x: x**2, range(10)))
等同與:
squares = [x**2 for x in range(10)]
這更簡潔易懂。 列表解析式在for前後再添加個for或者if。新生成的list會由最外圍中括號的for if來計算。舉個例子,以下程式碼混合了2個list中互相不等的元素到新的list。
[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
等於
combs = []
for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
如果[]中的表示式是tuple,像前面例子 (x,y),它必須是在括號內。
vec = [-4, -2, 0, 2, 4]
# create a new list with the values doubled
[x*2 for x in vec]
[-8, -4, 0, 4, 8]
# filter the list to exclude negative numbers
[x for x in vec if x >= 0]
[0, 2, 4]
# apply a function to all the elements
[abs(x) for x in vec]
[4, 2, 0, 2, 4]
# call a method on each element
freshfruit = [' banana', ' loganberry ', 'passion fruit ']
[weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
# create a list of 2-tuples like (number, square)
[(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
# the tuple must be parenthesized, otherwise an error is raised
[x, x**2 for x in range(6)]
File "", line 1, in
[x, x**2 for x in range(6)]
^
SyntaxError: invalid syntax
# flatten a list using a listcomp with two 'for'
vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
list列表表示式中科院包含更復雜的表達和運算:
from math import pi
[str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
5.1.4 列表解析式的細節
最初的列表解析式中科院包含任何二元運算子,包括另一個列表解析式。下面這個list是一個3*4的矩陣:
matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
以下程式碼將上面的3*4矩陣進行轉置:
[[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
就像我們在上一節看到的,內建的列表解析式其實就是for中的上下文操作,上面的例子等於:
transposed = []
for i in range(4):
... transposed.append([row[i] for row in matrix])
...
transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
同樣的,也等於:
transposed = []
for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
在真實程式碼場景中,你更應該使用Python的內建方法進行以上覆雜的操作。zip()起相同的作用:
list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
看 Unpacking Argument Lists 這一節能得到上面程式碼的更多細節。
5.2 刪除語句
根據次序移除list中的元素。與pop()不同,pop()返回彈出的元素,它不返回。del還可以切片的刪除list元素,或者清空整個list,早先我們有個這樣的例子。
a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
a
[1, 66.25, 333, 333, 1234.5]
del a[2:4]
a
[1, 66.25, 1234.5]
del a[:]
a
[]
del 可以刪除完整的變數 del a
刪除變數a之後我們再引用該變數會報錯,我們之後還會探索del的其他用法。
5.3 Tuple 和 Sequence
我們注意到list和字串有許多共同點,比如說索引和切片操作。以下是2個序列資料型別的例子(具體請看Sequence Types — list, tuple, range)。由於Python一直在進化,也許會有更多的序列資料型別加入。下面是另外一種標準庫裡的序列資料型別:tuple.一個tuple包含了一系列被逗號隔開的資料,比如說:
t = 12345, 54321, 'hello!'
t[0]
12345
t
(12345, 54321, 'hello!')
# Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
# Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object does not support item assignment
# but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
v
([1, 2, 3], [3, 2, 1])
輸出tuple總是被括號包圍,它裡面的tuple有木有括號都能被解釋正確;即使括號是必須的,它們的輸入tuple也不一定有括號包圍。不可以對tuple中的單個元素複製,但是,tuple中的元素是可變物件時,可以被改變,比如說list。 即使tuple和list看著蠻像,但它們通常在不同的情景不同的目的下使用。Tuple是不可變的,通常是包含了各種各樣的序列元素,這些元素可以通過解包(後面會提到)或者指定位置來觸達。List是可變的,元素通常是同一類的,我們可以通過迭代器遍歷整個list。 必須提到的是,構造tuple的時候。空tuple由一對括號初始化,1個元素的tuple初始化應當由1個值和逗號組成。看著醜但是有效哦。
empty = ()
singleton = 'hello', # <-- note trailing comma
len(empty)
0
len(singleton)
1
singleton
('hello',)
t = 12345, 54321, 'hello!'
是tuple解包的例子,12345,54321,’hello’都是tuple的元素。反過來的操作如下: x, y, z = t
以上序列解包操作適用於任何等式右邊的序列。序列解包操作要求等式左邊的變數個數等同於右邊序列的原色個數。當然多元賦值也是可以的,只是右邊序列解包操作出來的元素混進tuple輸出。
5.4 集合(Sets)
Python引入Set這種無序的,無重複元素的資料結構。我們通常用它來消除重複元素和進行隸屬測試。Set也支援一些數學操作 ,交,並,差,對稱差。 用中括號或set()來新建sets,需要注意的是,新建空set必須使用set()而不是{},後者是新建dict,我們下一章再討論dict。 下面是set的簡要使用:
basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket) # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
'orange' in basket # fast membership testing
True
'crabgrass' in basket
False
# Demonstrate set operations on unique letters from two words
...
a = set('abracadabra')
b = set('alacazam')
a # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
a - b # letters in a but not in b
{'r', 'd', 'b'}
a | b # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
a & b # letters in both a and b
{'a', 'c'}
a ^ b # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}
與list列表解析式類似,set也支援這樣操作:
a = {x for x in 'abracadabra' if x not in 'abc'}
a
{'r', 'd'}
5.5 字典(Dictionaries)
另一個很有用的Python內建資料結構是字典。Dictionaries有時在其他計算機語言裡,作為對應儲存,對應序列。不比被順序索引的序列資料型別,Dictionaries是不可變的結構,它被key索引,字串,數字經常被用作key。Tuple也可以被用作key,只要它僅由數字,字串或者tuple組成,如果該Tuple直接或間接包含可變物件,那它就不能被用作dictionary的key了。也不能把list用作dictionary的key,因為list不需要額外空間就能被指定索引修改,切片或appen(),enxtend()這樣的方法。 對dictionary最好的理解是一個無序的鍵值對,key在這個dictionary是獨一無二的。一對中括號可以建立一個空dictionary。用逗號分隔,按照key:value這樣的格式,由{}包圍,可以初始化一個dictionary,這也是dictionary輸出的格式。 dictionary的主要用法是儲存鍵值對以及通過key拿到value。通過del也能刪除一個鍵值對。如果1個key已經使用,再使用它的值會被更新。用一個不存在的鍵來取值會報錯。 list(d.keys())
可以以list的形式隨機隨機順序獲取dictionary的鍵,當然可以使用sorted(d.keys()獲取順序鍵list。想知道一個鍵是否存在於dictionary,使用in
。 這裡有些dictionary使用的小例子:
python
tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
tel['jack']
4098
del tel['sape']
tel['irv'] = 4127
tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
list(tel.keys())
['irv', 'guido', 'jack']
sorted(tel.keys())
['guido', 'irv', 'jack']
'guido' in tel
True
'jack' not in tel
False
dict()
可以從鍵值對序列構建一個dictionary:
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
dictionary解析式可以用鍵值對來建立dictionary。
{x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
當鍵是簡單字串時,經常用關鍵詞等式來建立1個dict。
dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
5.6 遍歷方法
通過items()
可以同時遍歷dictionary的鍵值對。
knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
... print(k, v)
gallahad the pure
robin the brave
enumerate()
可以遍歷序列同時得到索引和對應元素
for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
要同時遍歷2個或者更多的序列資料結構,可以通過zip()
。
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
要反向遍歷序列,首先要指定list頭,然後把改list作為reversed()
的引數執行。
for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
要遍歷一個序列,使用sorted
對原始list操作會輸出一個新的有序list,但是原始list不變。
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
在遍歷list的時候有時候想修改它,與其修改不如建立個新的list,這樣更簡單更方便。
import math
raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
filtered_data = []
for value in raw_data:
... if not math.isnan(value):
... filtered_data.append(value)
...
filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
5.7 條件語句
if和while語句中包含的條件語句,可不僅僅只是比較,可以是任何操作符。 in 和not操作是原來確認值是否存在於序列中。is和is not是確認2個例項物件是否相同;它僅可以作用於可變物件例如說list這樣。所有的比較操作符都有相同的運算優先順序,都低於數字操作符。 比較操作可以連起來。比如說a<b==c
是測試a<b且b=c。 比較操作符可以和二元操作符一起使用:and, o,比較操作符的結果可以被not顛倒。and, or, not的運算優先順序均低於比較操作符,其中,not是有最高優先順序,or是最低的,所以,A and not B or C
等同於(A and (not B)) or C。通常括號能展現它們的各自組成。 and, or這倆二元操作符被稱為短路操作符,引數由左到右運算,有結果輸出。如果A and B是true, 那即使B是flase, A and B and C
也不會去計算C的結果。當二元操作符被用作普通值運算而不是二元運算,短路操作的返回值就是最後一個引數。 可以吧比操作運算和其他二元運算的結果賦值給一個變數
string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
non_null = string1 or string2 or string3
non_null
Trondheim
不像C語言,Python的複製操作不能在操作符運算中出現。C語言開發者也許會對此困惑,但這條禁令有效的避免了C語言中常常出現的一種錯誤:在表示式中輸入=
而在此場景中需要的是==
。
5.8. 序列結構和其他資料結構型別的比較操作
序列結構例項也許會和另外的相同資料結構的例項進行比較操作。比較操作會按照字母順序:2個序列的第一個元素比較,如果它們不同,那輸出比較操作;如果相同,下一對元素進行比較,直到一方元素遍歷結束。如果是相同型別的序列結構,字母級比較操作還會遞迴進行。如果所有的元素都相同,那我們任務這倆序列結構的例項是相同的。如果一個是另外一個的子集,那短的就是小的那一個。字母級比較使用字串的Unicode編碼順序來比較。下面是例子
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
比較2個不同資料型別的例項也是可以的,只要比較的物件都有合適的方法。比如說不同的數字型別比較時,比較物件會轉換成它們的數值,如0變成0.0。除此之外,直譯器都會報TypeError異常。