網(wǎng)站建設員官網(wǎng)設計比較好看的網(wǎng)站
2.1 內(nèi)置類型序列概覽
容器序列(能存放不同類型的數(shù)據(jù)):(作者分的類)
list、tuple 和 collections.deque
扁平序列(只能容納一種類型):
str、byes、bytearray、memoryview 和 array.array
可變:
list、bytearray、array.array、collections.deque 和 memoryview
不可變:
tuple、str 和 bytes
2.2 列表推導和生成器表達式
很多 Python 程序員都把列表推導(list comprehension)簡稱為 listcomps,生成器表達式(generator expression)則稱為 genexps。
Python 會忽略代碼里 []、{} 和 () 中的換行
2.2.1 列表推導和可讀性
l2 = [fc(itm) for itm in l1]
2.2.2 列表推導同 filter 和 map 的比較
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
2.2.3 笛卡兒積
tshirts = [(color, size) for color in colors for size in sizes]
2.2.4 生成器表達式
生成器表達式的語法跟列表推導差不多,只不過把方括號換成圓括號而已。
tuple(ord(symbol) for symbol in symbols)
生成器表達式之后, 內(nèi)存里不會留下一個組合的列表,因為生成器表達式會在每次 for 循環(huán)運行時才生成一個組合。
2.3 元組不僅僅是不可變的列表
2.3.1 元組和記錄
2.3.2 元組拆包
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates # 元組拆包
b, a = a, b
用 * 來處理剩下的元素
>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])>>> a, *body, c, d = range(5)
>>> a, body, c, d
(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)
>>> head, b, c, d
([0, 1], 2, 3, 4)
2.3.3 嵌套元組拆包
name, cc, pop, (latitude, longitude) = ('Tokyo','JP',36.933,(35.689722,139.691667))
2.3.4 具名元組
collections.namedtuple 是一個工廠函數(shù)。用 namedtuple 構建的類的實例所消耗的內(nèi)存跟元組是一樣的,因為字段名都 被存在對應的類里面。這個實例跟普通的對象實例比起來也要小一些,因為 Python 不會用 __dict__
來存放這些實例的屬性。
創(chuàng)建一個具名元組需要兩個參數(shù),一個是類名,另一個是類的各個字段的名字。后者可 以是由數(shù)個字符串組成的可迭代對象,或者是由空格分隔開的字段名組成的字符串
from collections import namedtupleCity = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
元組有一些自己專有的屬性。展示了幾個最有用的:_fields 類屬性、類方法 _make(iterable) 和實例方法 _asdict()。
>>> City._fields
('name', 'country', 'population', 'coordinates')
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889)) >>> delhi = City._make(delhi_data)
>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
2.3.5 作為不可變列表的元組
除了跟增減元素相關的方法之外,元組支持列表的其他所有方法。還有一個例 外,元組沒有 __reversed__
方法,但是這個方法只是個優(yōu)化而已,reversed(my_tuple) 這 個用法在沒有 __reversed__
的情況下也是合法的。
2.4 切片
2.4.1 為什么切片和區(qū)間會忽略最后一個元素
在切片和區(qū)間操作里不包含區(qū)間范圍的最后一個元素是 Python 的風格,這個習慣符合
Python、C 和其他語言里以 0 作為起始下標的傳統(tǒng)。
2.4.2 對對象進行切片
>>> s = 'bicycle'
>>> s[::3]
'bye'
>>> s[::-1]
'elcycib'
>>> s[::-2]
'eccb'
可以給切片起名字增強可讀性!:
SKU = slice(0, 6)
print(item[SKU]
2.4.3 多維切片和省略
省略(ellipsis)。ellipsis 是類名,全小寫,而它的內(nèi)置實例寫作 Ellipsis。這其實跟 bool 是小寫,但是它的兩個實例寫作 True 和 False 異曲同工。
2.4.4 給切片賦值
對原序列就地修改!如果賦值的對象是一個切片,那么賦值語句的右側必須是個可迭代對象。即便只有單獨 一個值,也要把它轉換成可迭代的序列。
2.5 對序列使用 + 和 *
>>> l = [1, 2, 3]
>>> l * 5
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> 5 * 'abcd'
'abcdabcdabcdabcdabcd'
+ 和 * 都遵循這個規(guī)律,不修改原有的操作對象,而是構建一個全新的序列。
如果在a * n這個語句中,序列a里的元素是對其他可變對象的引用的話, 你就需要格外注意了,因為這個式子的結果可能會出乎意料。比如,你想用 my_list = [[]] * 3來初始化一個由列表組成的列表,但是你得到的列表里 包含的 3 個元素其實是 3 個引用,而且這 3 個引用指向的都是同一個列表。 這可能不是你想要的效果。
正確方式:
board = [['_'] * 3 for i in range(3)]
2.6 序列的增量賦值
+= 背后的特殊方法是 __iadd__
(用于“就地加法”)。但是如果一個類沒有實現(xiàn)這個方法的
話,Python 會退一步調(diào)用 __add__
。
如果 a 實現(xiàn)了 iadd 方法,就會調(diào)用這個方法。同時對可變序列(例如 list、 bytearray 和 array.array)來說,a 會就地改動,就像調(diào)用了 a.extend(b) 一樣。但是如 果 a 沒有實現(xiàn) iadd 的話,a += b 這個表達式的效果就變得跟 a = a + b 一樣了:首先 計算 a + b,得到一個新的對象,然后賦值給 a。也就是說,在這個表達式中,變量名會不 會被關聯(lián)到新的對象,完全取決于這個類型有沒有實現(xiàn) iadd 這個方法。而不可變序列根 本就不支持這個操作,對這個方法的實現(xiàn)也就無從談起。
>>> l = [1, 2, 3]
>>> id(l) 4311953800
>>> l *= 2
>>> l
[1, 2, 3, 1, 2, 3]
>>> id(l)
4311953800
>>> t = (1, 2, 3)
>>> id(t)
4312681568
>>> t *= 2
>>> id(t)
4301348296
一個關于+=的謎題
>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
到底會發(fā)生下面 4 種情況中的哪一種?
- a. t 變成 (1, 2, [30, 40, 50, 60])。
- b. 因為 tuple 不支持對它的元素賦值,所以會拋出 TypeError 異常。
- c. 以上兩個都不是。
- d. a 和 b 都是對的。
答案是 d
>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60])
- 將 s[a] 的值存入 TOS(Top Of Stack,棧的頂端)。
- 計算 TOS += b。這一步能夠完成,是因為 TOS 指向的是一個可變對象。
- s[a] = TOS 賦值。這一步失敗,是因為 s 是不可變的元組。
2.7 list.sort方法和內(nèi)置函數(shù)sorted
list.sort 方法會就地排序列表,返回 None。sorted,它會新建一個列表作為返回值。
兩個可選的關鍵字參數(shù)。
reverse:
如果被設定為 True,被排序的序列里的元素會以降序輸出(也就是說把最大值當作最 小值來排序)。這個參數(shù)的默認值是 False。
key:
一個只有一個參數(shù)的函數(shù),這個函數(shù)會被用在序列里的每一個元素上,所產(chǎn)生的結果 將是排序算法依賴的對比關鍵字。
可選參數(shù) key 還可以在內(nèi)置函數(shù) min() 和 max() 中起作用。另外,還有些標準庫里的函數(shù)也接受這個參數(shù),像 itertools.groupby() 和 heapq.nlargest() 等。
2.8 用bisect來管理已排序的序列
import bisect
2.8.1 用bisect來搜索
bisect 函數(shù)其實是 bisect_right 函數(shù)的別名,后者還有個姊妹函數(shù)叫 bisect_left。 它們的區(qū)別在于,bisect_left 返回的插入位置是原序列中跟被插入元素相等的元素的位置, 也就是新元素會被放置于它相等的元素的前面,而 bisect_right 返回的則是跟它相等的元素之后的位置。
兩個可選參數(shù) lo 和 hi 來縮小搜尋的范圍。lo 的默認值是 0,hi 的默認值是序列的長度,即 len() 作用于該序列的返回值。
2.8.2 用bisect.insort插入新元素
bisect.insort(my_list, new_item)
insort 跟 bisect 一樣,有 lo 和 hi 兩個可選參數(shù)用來控制查找的范圍。也有個變體叫 insort_left。
2.9 當列表不是首選時
2.9.1 數(shù)組
速度非???/p>
from array import arrayfloats = array('d', (random() for i in range(10**7)))
fp = open('floats.bin', 'wb')
floats.tofile(fp)
fp.close()floats2 = array('d')
fp = open('floats.bin', 'rb')
floats2.fromfile(fp, 10**7)
fp.close()
從 Python 3.4 開始,數(shù)組(array)類型不再支持諸如 list.sort() 這種就地排序方法。要給數(shù)組排序的話,得用 sorted 函數(shù)新建一個數(shù)組: a = array.array(a.typecode, sorted(a))
想要在不打亂次序的情況下為數(shù)組添加新的元素,bisect.insort 還是能派上用場。
2.9.2 內(nèi)存視圖
低級視角
numbers = array.array('h', [-2, -1, 0, 1, 2]) >>> memv = memoryview(numbers)
len(memv)
# 5
>>> memv[0]
# -2
memv_oct = memv.cast('B')
memv_oct.tolist()
# [254, 255, 255, 255, 0, 0, 1, 0, 2, 0] >>> memv_oct[5] = 4
numbers
# array('h', [-2, -1, 1024, 1, 2])
2.9.3 NumPy 和 SciPy
2.9.4 雙向隊列和其他形式的隊列
collections.deque 類(雙向隊列)是一個線程安全、可以快速從兩端添加或者刪除元素的數(shù)據(jù)類型。
dq = deque(range(10), maxlen=10)
maxlen 無法修改。
extendleft(iter) 方法會把迭代器里的元素逐個添加到雙向隊列的左邊,因此迭代器里的元素會逆序出現(xiàn)在隊列里。
>>> from collections import deque
>>> dq = deque(range(10), maxlen=10)
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3)
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1)
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10) >>> dq.extend([11, 22, 33])
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10) >>> dq.extendleft([10, 20, 30, 40])
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)
append 和 popleft 都是原子操作,也就說是 deque 可以在多線程程序中安全地當作先進先 出的隊列使用,而使用者不需要擔心資源鎖的問題。
queue
提供了同步(線程安全)類 Queue、LifoQueue 和 PriorityQueue,不同的線程可以利用 這些數(shù)據(jù)類型來交換信息。這三個類的構造方法都有一個可選參數(shù) maxsize,它接收正 整數(shù)作為輸入值,用來限定隊列的大小。但是在滿員的時候,這些類不會扔掉舊的元素 來騰出位置。相反,如果隊列滿了,它就會被鎖住,直到另外的線程移除了某個元素而 騰出了位置。這一特性讓這些類很適合用來控制活躍線程的數(shù)量。
multiprocessing
這個包實現(xiàn)了自己的 Queue,它跟 queue.Queue 類似,是設計給進程間通信用的。同時還有一個專門的 multiprocessing.JoinableQueue 類型,可以讓任務管理變得更方便。
asyncio
Python 3.4 新 提 供 的 包, 里 面 有 Queue、LifoQueue、PriorityQueue 和 JoinableQueue, 這些類受到 queue 和 multiprocessing 模塊的影響,但是為異步編程里的任務管理提供 了專門的便利。
heapq
跟上面三個模塊不同的是,heapq 沒有隊列類,而是提供了 heappush 和 heappop 方法,讓用戶可以把可變序列當作堆隊列或者優(yōu)先隊列來使用。