2.1 列表
- 序列結構有列表、元組、字串,字典、集合以及 range 等。
- 列表、元組、字串支援雙向索引,第一個元素下標為 $0$,第二個元素下標為 $1$,以此類推;最後一個元素下標為 $-1$,倒數第二個元素下標為 $-2$,以此類推。
- 列表是內建有序可變序列,列表元素放在中括號 “[]” 中,用逗號分隔;
- 列表元素增加或刪除時,列表物件自動進行擴充套件或收縮記憶體,保證元素之間沒有縫隙;
- 列表中資料型別可各不相同;
[10, 20, 30, 40]
['crunchy frog', 'ram bladder', 'lark vomit']
['spam', 2.0, 5, [10, 20]]
[['file1', 200,7], ['file2', 260,9]]
- 列表常用方法
2.1.1 列表建立與刪除
- 列表賦值;
>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list = [] #建立空列表
- 使用 list() 函式將 元組、range物件、字串 或其他型別的 可迭代物件型別 的資料轉換為列表;
>>> a_list = list((3,5,7,9,11))
>>> a_list
[3, 5, 7, 9, 11]
>>> list(range(1,10,2))
[1, 3, 5, 7, 9]
>>> list('hello world')
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> x = list() #建立空列表
- 使用 del 命令刪除整個列表;
>>> del a_list
>>> a_list
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
a_list
NameError: name 'a_list' is not defined
2.1.2 列表元素的增加
- 使用 “+” 運算子將元素新增到列表中;
>>> aList = [3,4,5]
>>> aList = aList + [7]
>>> aList
[3, 4, 5, 7]
- 列表物件的 append() 方法,原地修改列表,在列表尾部新增元素,速度較快;
>>> aList.append(9)
>>> aList
[3, 4, 5, 7, 9]
所謂 “原地”,是指不改變列表在記憶體中的首地址。
- 比較 “+” 和 append() 這兩種方法的速度差異。
import time
result = []
start = time.time()
for i in range(10000):
result = result + [i]
print(len(result), ',', time.time()-start)
result = []
start = time.time()
for i in range(10000):
result.append(i)
print(len(result), ',', time.time()-start)
- 修改值時,並不是真的直接修改變數的值,而是使變數指向新的值。
>>> a = [1,2,3]
>>> id(a) #返回物件的記憶體地址
20230752
>>> a = [1,2]
>>> id(a)
20338208
- 通過下標來修改序列中元素的值或通過可變序列物件自身提供的方法來增加和刪除元素時,序列物件在記憶體中的起始地址是不變的,僅僅是被改變值的元素地址發生變化,也就是所謂的“原地操作”。
>>> a = [1,2,4]
>>> b = [1,2,3]
>>> a == b
False
>>> id(a) == id(b)
False
>>> id(a[0]) == id(b[0])
True
>>> a = [1,2,3]
>>> id(a)
25289752
>>> a.append(4)
>>> id(a)
25289752
>>> a.remove(3)
>>> a
[1, 2, 4]
>>> id(a)
25289752
>>> a[0] = 5
>>> a
[5, 2, 4]
>>> id(a)
25289752
- 列表物件的 extend() 方法可將另一個迭代物件的所有元素新增至該列表物件尾部。通過 extend() 方法來增加列表元素也不改變其記憶體首地址,屬於原地操作。
>>> a.extend([7,8,9])
>>> a
[5, 2, 4, 7, 8, 9]
>>> aList.extend([11,13])
>>> aList
[3, 4, 5, 7, 9, 11, 13]
>>> aList.extend((15,17))
>>> aList
[3, 4, 5, 7, 9, 11, 13, 15, 17]
>>> id(a)
25289752
- 列表物件的 insert() 方法將元素新增至列表的指定位置。
>>> aList.insert(3, 6) #在下標為3的位置插入元素6
>>> aList
[3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
- 使用乘法來擴充套件列表物件,將列表與整數相乘,生成一個新列表,新列表是原列表中元素的重複。
>>> aList = [3,5,7]
>>> bList = aList
>>> id(aList)
57091464
>>> id(bList)
57091464
>>> aList = aList*3
>>> aList
[3, 5, 7, 3, 5, 7, 3, 5, 7]
>>> bList
[3,5,7]
>>> id(aList)
57092680
>>> id(bList)
57091464
- 使用 * 運算子 將包含列表的列表重複並建立新列表時,並不建立元素的複製,而是建立已有物件的引用。因此,當修改其中一個值時,相應的引用也會被修改。
>>> x = [[None] * 2] * 3
>>> x
[[None, None], [None, None], [None, None]]
>>> x[0][0] = 5
>>> x
[[5, None], [5, None], [5, None]]
>>> x = [[1,2,3]] * 3
>>> x[0][0] = 10
>>> x
[[10, 2, 3], [10, 2, 3], [10, 2, 3]]
2.1.3 列表元素的刪除
- del 命令刪除列表中的指定位置上的元素。
>>> a_list = [3,5,7,9,11]
>>> del a_list[1]
>>> a_list
[3, 7, 9, 11]
- 列表物件的 pop() 方法刪除並返回指定(預設為最後一個)位置上的元素,如果給定的索引超出列表的範圍則丟擲異常。
>>> a_list = list((3,5,7,9,11))
>>> a_list.pop()
11
>>> a_list
[3, 5, 7, 9]
>>> a_list.pop(1)
5
>>> a_list
[3, 7, 9]
- 列表物件的 remove() 方法刪除首次出現的指定元素,如果列表中不存在要刪除的元素,則丟擲異常。
>>> a_list = [3,5,7,9,7,11]
>>> a_list.remove(7)
>>> a_list
[3, 5, 9, 7, 11]
- 刪除對比
>>> x = [1,2,1,2,1,2,1,2,1]
>>> for i in x:
if i == 1:
x.remove(i)
>>> x
[2, 2, 2, 2]
>>> x = [1,2,1,2,1,1,1]
>>> for i in x:
if i == 1:
x.remove(i)
>>> x
[2, 2, 1]
正確的程式碼
>>> x = [1,2,1,2,1,1,1]
>>> for i in x[::]: #切片
if i == 1:
x.remove(i)
#或者:
>>> x = [1,2,1,2,1,1,1]
>>> for i in range(len(x)-1,-1,-1):
if x[i]==1:
del x[i]
2.1.4 列表元素訪問與計數
- 使用下標直接訪問列表元素,如果指定下標不存在,則丟擲異常。
>>> aList[3]
6
>>> aList[3] = 5.5
>>> aList
[3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17]
>>> aList[15]
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
aList[15]
IndexError: list index out of range
- 使用列表物件的 index() 方法獲取指定元素首次出現的下標,若列表物件中不存在指定元素,則丟擲異常。
>>> aList
[3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17]
>>> aList.index(7)
4
>>> aList.index(100)
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
aList.index(100)
ValueError: 100 is not in list
- 使用列表物件的 count() 方法統計指定元素在列表物件中出現的次數
>>> aList
[3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17]
>>> aList.count(7)
1
>>> aList.count(0)
0
>>> aList.count(8)
0
2.1.5 成員資格判斷
- 如果需要判斷列表中是否存在指定的值,可以使用 count() 方法,如果存在則返回大於 0 的數,如果返回 0 則表示不存在。或者,使用更加簡潔的“in”關鍵字來判斷一個值是否存在於列表中,返回結果為“True”或“False”。
>>> aList
[3, 4, 5, 5.5, 7, 9, 11, 13, 15, 17]
>>> 3 in aList
True
>>> 18 in aList
False
>>> bList = [[1], [2], [3]]
>>> 3 in bList
False
>>> 3 not in bList
True
>>> [3] in bList
True
>>> aList = [3, 5, 7, 9, 11]
>>> bList = ['a', 'b', 'c', 'd']
>>> (3, 'a') in zip(aList, bList)
True
>>> for a, b in zip(aList, bList):
print(a, b)
2.1.6 切片操作
- 切片使用 2 個冒號分隔的3個數字來完成,第一個數字表示切片開始位置(預設為0),第二個數字表示切片截止(但不包含)位置(預設為列表長度),第三個數字表示切片的步長(預設為1),當步長省略時可以順便省略最後一個冒號。
- 切片操作不會因為下標越界而丟擲異常,而是簡單地在列表尾部截斷或者返回一個空列表
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> aList[::] #返回包含元素的新列表
[3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> aList[::-1] #逆序的所有元素
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
>>> aList[::2] #偶數位置,隔一個取一個
[3, 5, 7, 11, 15]
>>> aList[1::2] #奇數位置,隔一個取一個
[4, 6, 9, 13, 17]
>>> aList[3::] #從下標3開始的所有元素
[6, 7, 9, 11, 13, 15, 17]
>>> aList[3:6] #下標在[3, 6)之間的所有元素
[6, 7, 9]
>>> aList[0:100:1] #前100個元素,自動截斷
[3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> a[100:] #下標100之後的所有元素,自動截斷
[]
>>> x[100] #直接使用下標訪問會發生越界
IndexError: list index out of range
- 可以使用切片來原地修改列表內容
>>> aList = [3, 5, 7]
>>> aList[len(aList):] = [9] #在尾部追加元素
>>> aList
[3, 5, 7, 9]
>>> aList[:3] = [1, 2, 3] #替換前3個元素
>>> aList
[1, 2, 3, 9]
>>> aList[:3] = [] #刪除前3個元素
>>> aList
[9]
>>> aList = list(range(10))
>>> aList
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> aList[::2] = [0]*5 #替換偶數位置上的元素
>>> aList
[0, 1, 0, 3, 0, 5, 0, 7, 0, 9]
>>> aList[::2] = [0]*3 #切片不連續,兩個元素個數必須一樣多
ValueError: attempt to assign sequence of size 3 to extended slice of size 5
- 使用 del 與切片結合來刪除列表元素
>>> aList = [3,5,7,9,11]
>>> del aList[:3] #刪除前3個元素
>>> aList
[9, 11]
>>> aList = [3,5,7,9,11]
>>> del aList[::2] #刪除偶數位置上的元素
>>> aList
[5, 9]
- 切片返回的是列表元素的淺複製
>>> aList = [3, 5, 7]
>>> bList = aList #bList與aList指向同一個記憶體
>>> bList
[3, 5, 7]
>>> bList[1] = 8 #修改其中一個物件會影響另一個
>>> aList
[3, 8, 7]
>>> aList == bList #兩個列表的元素完全一樣
True
>>> aList is bList #兩個列表是同一個物件
True
>>> id(aList) #記憶體地址相同
19061816
>>> id(bList)
19061816
- 所謂淺複製,是指生成一個新的列表,並且把原列表中所有元素的引用都複製到新列表中。
>>> aList = [3, 5, 7]
>>> bList = aList[::] #切片,淺複製
>>> aList == bList #兩個列表的元素完全一樣
True
>>> aList is bList #但不是同一個物件
False
>>> id(aList) == id(bList) #記憶體地址不一樣
False
>>> bList[1] = 8 #修改其中一個不會影響另一個
>>> bList
[3, 8, 7]
>>> aList
[3, 5, 7]
2.1.7 列表排序
- 使用列表物件的 sort 方法進行原地排序,支援多種不同的排序方法。
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> import random
>>> random.shuffle(aList)
>>> aList
[3, 4, 15, 11, 9, 17, 13, 6, 7, 5]
>>> aList.sort() #預設是升序排序
>>> aList.sort(reverse = True) #降序排序
>>> aList
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
>>> aList.sort(key = lambda x:len(str(x))) #按轉換成字串的長度排序
>>> aList
[9, 7, 6, 5, 4, 3, 17, 15, 13, 11]
- 使用內建函式sorted對列表進行排序並返回新列表
>>> aList
[9, 7, 6, 5, 4, 3, 17, 15, 13, 11]
>>> sorted(aList) #升序排序
[3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> sorted(aList,reverse = True) #降序排序
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
- 列表物件的 reverse 方法將元素原地逆序
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> aList.reverse()
>>> aList
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
- 內建函式 reversed 方法對列表元素進行逆序排列並返回迭代物件
>>> aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
>>> newList = reversed(aList) #返回reversed物件
>>> list(newList) #把reversed物件轉換成列表
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
>>> for i in newList:
print(i, end=' ') #這裡沒有輸出內容
#迭代物件已遍歷結束
>>> newList = reversed(aList) #重新建立reversed物件
>>> for i in newList:
print(i, end=' ')
17 15 13 11 9 7 6 5 4 3
2.1.8 用於序列操作的常用內建函式
- len(列表):返回列表中的元素個數,同樣適用於元組、字典、集合、字串等。
- max(列表)、 min(列表):返回列表中的最大或最小元素,同樣適用於元組、字典、集合、range物件等。
- sum(列表):對列表的元素進行求和運算,對非數值型列表運算需要指定start引數,同樣適用於元組、range。
>>> sum(range(1, 11)) #sum()函式的start引數預設為0
55
>>> sum(range(1, 11), 5) #指定start引數為5,等價於5+sum(range(1,11))
60
>>> sum([[1, 2], [3], [4]], []) #這個操作佔用空間較大,慎用
[1, 2, 3, 4]
- zip() 函式返回可迭代的 zip 物件。
>>> aList = [1, 2, 3]
>>> bList = [4, 5, 6]
>>> cList = zip(a, b) #返回zip物件
>>> cList
<zip object at 0x0000000003728908>
>>> list(cList) #把zip物件轉換成列表
[(1, 4), (2, 5), (3, 6)]
- enumerate(列表):列舉列表元素,返回列舉物件,其中每個元素為包含下標和值的元組。該函式對元組、字串同樣有效。
>>> for item in enumerate('abcdef'):
print(item)
2.1.9 列表推導式
- 列表推導式使用非常簡潔的方式來快速生成滿足特定需求的列表
>>> aList = [x*x for x in range(10)]
相當於
>>> aList = []
>>> for x in range(10):
aList.append(x*x)
也相當於
>>> aList = list(map(lambda x: x*x, range(10)))
例子:
>>> sum([2**i for i in range(64)])
18446744073709551615
- 使用列表推導式實現巢狀列表的平鋪
>>> 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]
相當於
>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> result = []
>>> for elem in vec:
for num in elem:
result.append(num)
>>> result
[1, 2, 3, 4, 5, 6, 7, 8, 9]
不使用列表推導式:
>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> sum(vec, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
#或
>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> from itertools import chain
>>> list(chain(*vec))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
- 列出當前資料夾下所有 Python 原始檔
>>> import os
>>> [filename for filename in os.listdir('.') if filename.endswith(('.py', '.pyw'))]
- 過濾不符合條件的元素
>>> aList = [-1,-4,6,7.5,-2.3,9,-11]
>>> [i for i in aList if i>0]
[6, 7.5, 9]
例子:已知有一個包含一些同學成績的字典,計算成績的最高分、最低分、平均分,並查詢所有最高分同學。
>>> scores = {"Zhang San": 45, "Li Si": 78, "Wang Wu": 40, "Zhou Liu": 96, "Zhao Qi": 65, "Sun Ba": 90, "Zheng Jiu": 78, "Wu Shi": 99, "Dong Shiyi": 60}
>>> highest = max(scores.values())
>>> lowest = min(scores.values())
>>> average = sum(scores.values())*1.0/len(scores)
>>> highest, lowest, average
99 40 72.33333333333333
>>> highestPerson = [name for name, score in scores.items() if score == highest]
>>> highestPerson
['Wu Shi']
- 在列表推導式中使用多個迴圈,實現多序列元素的任意組合,並且可以結合條件語句過濾特定元素
>>> [(x, y) for x in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
>>> [(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)]
- 使用列表推導式實現矩陣轉置
>>>matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
- 也可以使用內建函式來實現矩陣轉置
>>>list(zip(*matrix)) #序列解包
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
- 列表推導式中可以使用函式或複雜表示式
>>> def f(v):
if v%2 == 0:
v = v**2
else:
v = v+1
return v
>>> [f(v) for v in [2, 3, 4, -1] if v>0]
[4, 4, 16]
>>> [v**2 if v%2 == 0 else v+1 for v in [2, 3, 4, -1] if v>0]
[4, 4, 16]
- 列表推導式支援檔案物件迭代
>>> with open('C:\\RHDSetup.log', 'r') as fp:
print([line for line in fp])
- 使用列表推導式生成 100 以內的所有素數
>>> [p for p in range(2, 100) if 0 not in [p%d for d in range(2, int(p**0.5)+1)]]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
2.1.10 使用列表實現向量運算
>>> import random
>>> x = [random.randint(1,100) for i in range(10)] #生成隨機數
>>> list(map(lambda i: i+5, x)) #所有元素同時加5
>>> x = [random.randint(1,10) for i in range(10)]
>>> y = [random.randint(1,10) for i in range(10)]
>>> import operator
>>> sum(map(operator.mul, x, y)) #向量內積
>>> sum((i*j for i, j in zip(x, y))) #向量內積
>>> list(map(operator.add, x, y)) #兩個等長的向量對應元素相加
2.2 元組
2.2.1 元組建立與刪除
- 使用“=”將一個元組賦值給變數
>>>a_tuple = ('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a = (3)
>>> a
3
>>> a = (3,) #包含一個元素的元組,最後必須多寫個逗號
>>> a
(3,)
>>> a = 3, #也可以這樣建立元組
>>> a
(3,)
>>> x = () #空元組
- 使用 tuple 函式將其他序列轉換為元組
>>> tuple('abcdefg') #把字串轉換為元組
('a', 'b', 'c', 'd', 'e', 'f', 'g')
>>> aList
[-1, -4, 6, 7.5, -2.3, 9, -11]
>>> tuple(aList) #把列表轉換為元組
(-1, -4, 6, 7.5, -2.3, 9, -11)
>>> s = tuple() #空元組
>>> s
()
- 使用 del 可以刪除元組物件,不能刪除元組中的元素
2.2.2 元組與列表的區別
- 元組中的資料一旦定義就不允許更改。
- 元組沒有 append()、extend() 和 insert() 等方法,無法向元組中新增元素。
- 元組沒有 remove() 或 pop() 方法,也無法對元組元素進行 del 操作,不能從元組中刪除元素。
- 從效果上看,tuple( ) 凍結列表,而 list( ) 融化元組。
2.2.2 元組的優點
- 元組的速度比列表更快。
- 元組對不需要改變的資料進行“防寫”將使得程式碼更加安全。
- 元組可用作字典鍵(不可變資料的元組)。列表永遠不能當做字典鍵使用,因為列表不是不可變的。
2.2.3 序列解包
- 使用序列解包功能對多個變數同時賦值
>>> x, y, z = 1, 2, 3 #多個變數同時賦值
>>> v_tuple = (False, 3.5, 'exp')
>>> (x, y, z) = v_tuple
>>> x, y, z = v_tuple
>>> x, y, z = range(3) #可以對range物件進行序列解包
>>> x, y, z = iter([1, 2, 3]) #使用迭代器物件進行序列解包
>>> x, y, z = map(str, range(3)) #使用可迭代的map物件進行序列解包
>>> a, b = b, a #交換兩個變數的值
>>> x, y, z = sorted([1, 3, 2]) #sorted()函式返回排序後的列表
>>> a, b, c = 'ABC' #字串也支援序列解包
- 序列解包對於列表和字典同樣有效
>>> s = {'a':1, 'b':2, 'c':3}
>>> b, c, d = s.items()
>>> b
('c', 3)
>>> b, c, d = s #使用字典時不用太多考慮元素的順序
>>> b
'c'
>>> b, c, d = s.values()
>>> print(b, c, d)
1 3 2
- 序列解包遍歷多個序列
>>> keys = ['a', 'b', 'c', 'd']
>>> values = [1, 2, 3, 4]
>>> for k, v in zip(keys, values):
print((k, v), end=' ')
('a', 1) ('b', 2) ('c', 3) ('d', 4)
- 序列解包遍歷 enumerate 物件
>>> x = ['a', 'b', 'c']
>>> for i, v in enumerate(x):
print('The value on position {0} is {1}'.format(i,v))
The value on position 0 is a
The value on position 1 is b
The value on position 2 is c
>>> aList = [1,2,3]
>>> bList = [4,5,6]
>>> cList = [7,8,9]
>>> dList = zip(aList, bList, cList)
>>> for index, value in enumerate(dList):
print(index, ':', value)
0 : (1, 4, 7)
1 : (2, 5, 8)
2 : (3, 6, 9)
- Python 3.5還支援下面用法的序列解包
>>> print(*[1, 2, 3], 4, *(5, 6))
1 2 3 4 5 6
>>> *range(4),4
(0, 1, 2, 3, 4)
>>> {*range(4), 4, *(5, 6, 7)}
{0, 1, 2, 3, 4, 5, 6, 7}
>>> {'x': 1, **{'y': 2}}
{'y': 2, 'x': 1}
2.2.4 生成器推導式
- 生成器推導式的結果是一個生成器物件。使用生成器物件的元素時,可以根據需要將其轉化為列表或元組,也可以使用生成器物件 __next__() 方法或內建函式 next() 進行遍歷,或者直接將其作為迭代器物件來使用。
- 生成器物件具有惰性求值的特點,只在需要時生成新元素,比列表推導式具有更高的效率,空間佔用非常少,尤其適合大資料處理的場合。
- 不管用哪種方法訪問生成器物件,都無法再次訪問已訪問過的元素。
- 使用生成器物件__next__()方法或內建函式next()進行遍歷
>>> g = ((i+2)**2 for i in range(10)) #建立生成器物件
>>> g
<generator object <genexpr> at 0x0000000003095200>
>>> tuple(g) #將生成器物件轉換為元組
(4, 9, 16, 25, 36, 49, 64, 81, 100, 121)
>>> list(g) #生成器物件已遍歷結束,沒有元素了
[]
>>> g = ((i+2)**2 for i in range(10)) #重新建立生成器物件
>>> g.__next__() #使用生成器物件的__next__()方法獲取元素
4
>>> g.__next__() #獲取下一個元素
9
>>> next(g) #使用函式next()獲取生成器物件中的元素
16
- 使用生成器物件 __next__() 方法或內建函式 next() 進行遍歷
>>> g = ((i+2)**2 for i in range(10)) #建立生成器物件
>>> g
<generator object <genexpr> at 0x0000000003095200>
>>> tuple(g) #將生成器物件轉換為元組
(4, 9, 16, 25, 36, 49, 64, 81, 100, 121)
>>> list(g) #生成器物件已遍歷結束,沒有元素了
[]
>>> g = ((i+2)**2 for i in range(10)) #重新建立生成器物件
>>> g.__next__() #使用生成器物件的__next__()方法獲取元素
4
>>> g.__next__() #獲取下一個元素
9
>>> next(g) #使用函式next()獲取生成器物件中的元素
16
- 使用 for 迴圈直接迭代生成器物件中的元素
>>> g = ((i+2)**2 for i in range(10))
>>> for item in g: #使用迴圈直接遍歷生成器物件中的元素
print(item, end=' ')
4 9 16 25 36 49 64 81 100 121
>>> x = filter(None, range(20)) #filter物件也具有類似的特點
>>> 5 in x
True
>>> 2 in x #不可再次訪問已訪問過的元素
False
>>> x = map(str, range(20)) #map物件也具有類似的特點
>>> '0' in x
True
>>> '0' in x #不可再次訪問已訪問過的元素
False
2.3 字典
- 字典是無序可變序列。
- 定義字典時,每個元素的鍵和值用冒號分隔,元素之間用逗號分隔,所有的元素放在一對大括號“{}”中。
- 字典中的鍵可以為任意不可變資料,比如整數、實數、複數、字串、元組等等。
- globals() 返回包含當前作用域內所有全域性變數和值的字典
- locals() 返回包含當前作用域內所有區域性變數和值的字典
2.3.1 字典建立與刪除
- 使用=將一個字典賦值給一個變數
>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict
{'database': 'mysql', 'server': 'db.diveintopython3.org'}
>>> x = {} #空字典
>>> x
{}
- 使用 dict 利用已有資料建立字典:
>>> keys = ['a', 'b', 'c', 'd']
>>> values = [1, 2, 3, 4]
>>> dictionary = dict(zip(keys, values))
>>> dictionary
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> x = dict() #空字典
>>> x
{}
- 以給定內容為鍵,建立值為空的字典
>>> adict = dict.fromkeys(['name', 'age', 'sex'])
>>> adict
{'age': None, 'name': None, 'sex': None}
- 可以使用 del 刪除整個字典
2.3.2 字典元素的讀取
- 以鍵作為下標可以讀取字典元素,若鍵不存在則丟擲異常
>>> aDict = {'name':'Dong', 'sex':'male', 'age':37}
>>> aDict['name']
'Dong'
>>> aDict['tel'] #鍵不存在,丟擲異常
Traceback (most recent call last):
File "<pyshell#53>", line 1, in <module>
aDict['tel']
KeyError: 'tel'
- 使用字典物件的 get 方法獲取指定鍵對應的值,並且可以在鍵不存在的時候返回指定值。
>>> print(aDict.get('address'))
None
>>> print(aDict.get('address', 'SDIBT'))
SDIBT
>>> aDict['score'] = aDict.get('score',[])
>>> aDict['score'].append(98)
>>> aDict['score'].append(97)
>>> aDict
{'age': 37, 'score': [98, 97], 'name': 'Dong', 'sex': 'male'}
- 使用字典物件的 items() 方法可以返回字典的鍵、值對列表
- 使用字典物件的 keys() 方法可以返回字典的鍵列表
- 使用字典物件的 values() 方法可以返回字典的值列表
>>> aDict={'name':'Dong', 'sex':'male', 'age':37}
>>> for item in aDict.items(): #輸出字典中所有元素
print(item)
('age', 37)
('name', 'Dong')
('sex', 'male')
>>> for key in aDict: #不加特殊說明,預設輸出鍵
print(key)
age
name
sex
>>> for key, value in aDict.items(): #序列解包用法
print(key, value)
age 37
name Dong
sex male
>>> aDict.keys() #返回所有鍵
dict_keys(['name', 'sex', 'age'])
>>> aDict.values() #返回所有值
dict_values(['Dong', 'male', 37])
- 使用字典物件的update方法將另一個字典的鍵、值對新增到當前字典物件;
2.3.3 字典元素的新增與修改
- 當以指定鍵為下標為字典賦值時,若鍵存在,則可以修改該鍵的值;若不存在,則表示新增一個鍵、值對;
>>> aDict['age'] = 38 #修改元素值
>>> aDict
{'age': 38, 'name': 'Dong', 'sex': 'male'}
>>> aDict['address'] = 'SDIBT' #增加新元素
>>> aDict
{'age': 38, 'address': 'SDIBT', 'name': 'Dong', 'sex': 'male'}
- 使用字典物件的 update 方法將另一個字典的鍵、值對新增到當前字典物件
>>> aDict
{'age': 37, 'score': [98, 97], 'name': 'Dong', 'sex': 'male'}
>>> aDict.items()
dict_items([('age', 37), ('score', [98, 97]), ('name', 'Dong'), ('sex', 'male')])
>>> aDict.update({'a':'a','b':'b'})
>>> aDict
{'a': 'a', 'score': [98, 97], 'name': 'Dong', 'age': 37, 'b': 'b', 'sex': 'male'}
- 使用 del 刪除字典中指定鍵的元素;
- 使用字典物件的 clear() 方法來刪除字典中所有元素;
- 使用字典物件的 pop() 方法刪除並返回指定鍵的元素;
- 使用字典物件的 popitem() 方法刪除並返回字典中的一個元素;
2.3.5 有序字典
- Python內建字典是無序的,如需一個可以記住元素插入順序的字典,可以使用 collections.OrderedDict;
>>> x = dict() #無序字典
>>> x['a'] = 3
>>> x['b'] = 5
>>> x['c'] = 8
>>> x
{'b': 5, 'c': 8, 'a': 3}
>>> import collections
>>> x = collections.OrderedDict() #有序字典
>>> x['a'] = 3
>>> x['b'] = 5
>>> x['c'] = 8
>>> x
OrderedDict([('a', 3), ('b', 5), ('c', 8)])
2.3.6 字典推導式
>>> s = {x:x.strip() for x in (' he ', 'she ', ' I')}
>>> s
{' he ': 'he', ' I': 'I', 'she ': 'she'}
>>> for k, v in s.items():
print(k, ':', v)
he : he
I : I
she : she
>>> {i:str(i) for i in range(1, 5)}
{1: '1', 2: '2', 3: '3', 4: '4'}
>>> x = ['A', 'B', 'C', 'D']
>>> y = ['a', 'b', 'b', 'd']
>>> {i:j for i,j in zip(x,y)}
{'A': 'a', 'C': 'b', 'B': 'b', 'D': 'd'}
2.4 集合
- 集合是無序可變序列,使用一對大括號界定,元素不可重複,同一個集合中每個元素都是唯一的;
- 集合中只能包含數字、字串、元組等不可變型別(或者說可雜湊)的資料,而不能包含列表、字典、集合等可變型別的資料;
2.4.1 集合的建立與刪除
- 直接將集合賦值給變數
>>> a = {3, 5}
>>> a.add(7) #向集合中新增元素
>>> a
{3, 5, 7}
- 使用 set 將其他型別資料轉換為集合
>>> a_set = set(range(8,14))
>>> a_set
{8, 9, 10, 11, 12, 13}
>>> b_set = set([0, 1, 2, 3, 0, 1, 2, 3, 7, 8]) #自動去除重複
>>> b_set
{0, 1, 2, 3, 7, 8}
>>> c_set = set() #空集合
>>> c_set
set()
- 使用 del 刪除整個集合
- 當不再使用某個集合時,可以使用 del 命令刪除整個集合。集合物件的 pop() 方法彈出並刪除其中一個元素,remove() 方法直接刪除指定元素,clear() 方法清空集合。
2.4.2 集合操作
Python集合支援交集、並集、差集等運算
>>> a_set = set([8, 9, 10, 11, 12, 13])
>>> b_set = {0, 1, 2, 3, 7, 8}
>>> a_set | b_set #並集
{0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13}
>>> a_set.union(b_set) #並集
{0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13}
>>> a_set & b_set #交集
{8}
>>> a_set.intersection(b_set) #交集
{8}
>>> a_set.difference(b_set) #差集
{9, 10, 11, 12, 13}
>>> a_set - b_set
{9, 10, 11, 12, 13}
>>> a_set.symmetric_difference(b_set) #對稱差集
{0, 1, 2, 3, 7, 9, 10, 11, 12, 13}
>>> a_set ^ b_set
{0, 1, 2, 3, 7, 9, 10, 11, 12, 13}
>>> x = {1, 2, 3}
>>> y = {1, 2, 5}
>>> z = {1, 2, 3, 4}
>>> x.issubset(y) #測試是否為子集
False
>>> x.issubset(z)
True
>>> {3} & {4}
set()
>>> {3}.isdisjoint({4}) #如果兩個集合的交集為空,返回True
True
- 集合包含關係測試
>>> x = {1, 2, 3}
>>> y = {1, 2, 5}
>>> z = {1, 2, 3, 4}
>>> x < y #比較集合大小/包含關係
False
>>> x < z #真子集
True
>>> y < z
False
>>> {1, 2, 3} <= {1, 2, 3} #子集
True
- 使用集合快速提取序列中單一元素
>>> import random
>>> listRandom = [random.choice(range(10000)) for i in range(100)]
>>> noRepeat = []
>>> for i in listRandom :
if i not in noRepeat :
noRepeat.append(i)
>>> len(listRandom)
>>> len(noRepeat)
>>> newSet = set(listRandom)
2.5 再談內建方法sorted()
- 列表物件提供了 sort() 方法支援原地排序,而內建函式 sorted() 返回新的列表,並不對原列表進行任何修改。
- sorted() 方法可以對列表、元組、字典、range 物件等進行排序。
- 列表的 sort() 方法和內建函式 sorted() 都支援 key 引數實現複雜排序要求。
>>> persons = [{'name':'Dong', 'age':37}, {'name':'Zhang', 'age':40}, {'name':'Li', 'age':50}, {'name':'Dong', 'age':43}]
>>> print(persons)
[{'age': 37, 'name': 'Dong'}, {'age': 40, 'name': 'Zhang'}, {'age': 50, 'name': 'Li'}, {'age': 43, 'name': 'Dong'}]
#使用key來指定排序依據,先按姓名升序排序,姓名相同的按年齡降序排序
>>> print(sorted(persons, key=lambda x:(x['name'], -x['age'])))
[{'age': 43, 'name': 'Dong'}, {'age': 37, 'name': 'Dong'}, {'age': 50, 'name': 'Li'}, {'age': 40, 'name': 'Zhang'}]
>>> phonebook = {'Linda':'7750', 'Bob':'9345', 'Carol':'5834'}
>>> from operator import itemgetter
>>> sorted(phonebook.items(), key=itemgetter(1)) #按字典中元素值進行排序
[('Carol', '5834'), ('Linda', '7750'), ('Bob', '9345')]
>>> sorted(phonebook.items(), key=itemgetter(0)) #按字典中元素的鍵進行排序
[('Bob', '9345'), ('Carol', '5834'), ('Linda', '7750')]
>>> gameresult = [['Bob', 95.0, 'A'], ['Alan', 86.0, 'C'], ['Mandy', 83.5, 'A'], ['Rob', 89.3, 'E']]
>>> sorted(gameresult, key=itemgetter(0, 1)) #按姓名升序,姓名相同按分數升序排序
[['Alan', 86.0, 'C'], ['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Rob', 89.3, 'E']]
>>> sorted(gameresult, key=itemgetter(1, 0)) #按分數升序,分數相同的按姓名升序排序
[['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E'], ['Bob', 95.0, 'A']]
>>> sorted(gameresult, key=itemgetter(2, 0)) #按等級升序,等級相同的按姓名升序排序
[['Bob', 95.0, 'A'], ['Mandy', 83.5, 'A'], ['Alan', 86.0, 'C'], ['Rob', 89.3, 'E']]
>>> gameresult = [{'name':'Bob', 'wins':10, 'losses':3, 'rating':75.0},
{'name':'David', 'wins':3, 'losses':5, 'rating':57.0},
{'name':'Carol', 'wins':4, 'losses':5, 'rating':57.0},
{'name':'Patty', 'wins':9, 'losses':3, 'rating':72.8}]
>>> sorted(gameresult, key=itemgetter('wins', 'name'))
#按'wins'升序,該值相同的按'name'升序排序
[{'wins': 3, 'rating': 57.0, 'name': 'David', 'losses': 5}, {'wins': 4, 'rating': 57.0, 'name': 'Carol', 'losses': 5}, {'wins': 9, 'rating': 72.8, 'name': 'Patty', 'losses': 3}, {'wins': 10, 'rating': 75.0, 'name': 'Bob', 'losses': 3}]
- 根據另外一個列表的值來對當前列表元素進行排序
>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs = sorted(pairs)
>>> pairs
[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
>>> result = [x[1] for x in pairs]
>>> result
['else', 'sort', 'to', 'something']
2.6 複雜資料結構
2.6.1 堆
>>> import heapq #heapq和random是Python標準庫
>>> import random
>>> data=range(10)
>>> data
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.choice(data) #隨機選擇一個元素
9
>>> random.shuffle(data) #隨機打亂順序
>>> data
[6, 1, 3, 4, 9, 0, 5, 2, 8, 7]
>>> heap=[]
>>> for n in data: #建堆
heapq.heappush(heap,n)
>>> heap
[0, 2, 1, 4, 7, 3, 5, 6, 8, 9]
>>> heapq.heappush(heap,0.5) #入堆,自動重建
>>> heap
[0, 0.5, 1, 4, 2, 3, 5, 6, 8, 9, 7]
>>> heapq.heappop(heap) #出堆,自動重建
0
>>> myheap=[1,2,3,5,7,8,9,4,10,333]
>>> heapq.heapify(myheap) #建堆
>>> myheap
[1, 2, 3, 4, 7, 8, 9, 5, 10, 333]
>>> heapq.heapreplace(myheap,6) #彈出最小元素,同時插入新元素
1
>>> myheap
[2, 4, 3, 5, 7, 8, 9, 6, 10, 333]
>>> heapq.nlargest(3, myheap) #返回前3個最大的元素
[333, 10, 9]
>>> heapq.nsmallest(3, myheap) #返回前3個最小的元素
[2, 3, 4]
2.6.2 佇列
>>> import queue #queue是Python標準庫
>>> q=queue.Queue()
>>> q.put(0) #入隊
>>> q.put(1)
>>> q.put(2)
>>> q.queue
deque([0, 1, 2])
>>> q.get() #出隊
0
>>> q.queue #檢視佇列中的元素
deque([1, 2])
>>> q.get()
1
>>> q.queue
deque([2])
- queue模組還提供了“後進先出”佇列和優先順序佇列。
>>> from queue import Queue #LILO佇列
>>> q = Queue() #建立佇列物件
>>> q.put(0) #在佇列尾部插入元素
>>> q.put(1)
>>> q.put(2)
>>> print(q.queue) #檢視佇列中所有元素
deque([0, 1, 2])
>>> q.get() #返回並刪除佇列頭部元素
0
>>> q.get()
1
>>> from queue import LifoQueue #LIFO佇列
>>> q = LifoQueue() #建立LIFO佇列物件
>>> q.put(1) #在佇列尾部插入元素
>>> q.put(2)
>>> q.put(3)
>>> q.queue #檢視佇列中所有元素
[1, 2, 3]
>>> q.get() #返回並刪除佇列尾部元素
3
>>> q.get()
2
>>> q.queue
[1]
>>> q.get() #對空佇列呼叫get()方法會阻塞當前執行緒
>>> from queue import PriorityQueue #優先順序佇列
>>> q = PriorityQueue() #建立優先順序佇列物件
>>> q.put(3) #插入元素
>>> q.put(8) #插入元素
>>> q.put(100)
>>> q.queue #檢視優先順序佇列中所有元素
[3, 8, 100]
>>> q.put(1) #插入元素,自動調整優先順序佇列
>>> q.put(2)
>>> q.queue
[1, 2, 100, 8, 3]
>>> q.get() #返回並刪除優先順序最低的元素
1
>>> q.get() #請多執行幾次該語句並觀察返回的資料
2
- Python標準庫collections提供了雙端佇列deque
>>> from collections import deque
>>> q = deque(maxlen=5) #建立雙端佇列
>>> for item in [3, 5, 7, 9, 11]: #新增元素
q.append(item)
>>> q.append(13) #佇列滿,自動溢位
>>> q.append(15)
>>> q
deque([7, 9, 11, 13, 15], maxlen=5)
>>> q.appendleft(5) #從左側新增元素,右側自動溢位
>>> q
deque([5, 7, 9, 11, 13], maxlen=5)
2.6.3 棧
- 棧是一種“後進先出(LIFO)”或“先進後出(FILO)”的資料結構。
- Python列表本身就可以實現棧結構的基本操作。例如,列表物件的append()方法是在列表尾部追加元素,類似於入棧操作;pop()方法預設是彈出並返回列表的最後一個元素,類似於出棧操作。
- 但是直接使用Python列表物件模擬棧操作並不是很方便,例如當列表為空時再執行pop()出棧操作時則會丟擲一個不很友好的異常;另外,也無法限制棧的大小。
- 可以直接使用列表來實現棧結構
>>> myStack = []
>>> myStack.append(3)
>>> myStack.append(5)
>>> myStack.append(7)
>>> myStack
[3, 5, 7]
>>> myStack.pop()
7
>>> myStack.pop()
5
>>> myStack.pop()
3
>>> myStack.pop()
出錯
- 封裝列表實現棧結構
class Stack:
def __init__(self, size = 10):
self._content = [] #使用列表存放棧的元素
self._size = size #初始棧大小
self._current = 0 #棧中元素個數初始化為0
def empty(self):
self._content = []
self._current = 0
def isEmpty(self):
if not self._content:
return True
else:
return False
看完點個關注唄!!Growing