《流暢的python》讀書筆記(3)

第二章

2.1 內置序列類型概覽

python標準庫中的序列是由C實現的
按存放數據的類型分類:
容器序列:list、tuple 和 collections.deque 這些序列能存放不同類型的數據。容器序列存放的是它們所包含的任意類型的對象引用,注意,是引用。可以是不連續的
扁平序列:str、bytes、bytearray、memoryview 和 array.array,這類序列只能容納一種類型。扁平序列存放的是值,而不是引用,它是一段連續的內存空間。它裏面只能存放諸如字符,字節和數值這種基礎類型
按能否被修改來分類:
可變序列:list、bytearray、array.array、collections.deque 和 memoryview。
不可變序列:tuple、str 和 bytes。

2.2 列表推導和生成器表達式

列表推導:

列表推導是構建列表(list)的快捷方式
列表推導的一個範例如下

#普通for循環構造列表
symbols = '$%^&@!'
codes = []
for symbol in symbols:
	codes.append(ord(symbol))
print(codes)

#用列表推導構造列表
symbols = '$%^&@!'
codes = []
for symbol in symbols:
	codes.append(ord(symbol))
print(codes)

codes = []
codes = [ord(symbol) for symbol in symbols]
print(type(codes))
# 打印:<class 'list'> 可見列表推導生成的就是一個列表
print(codes)
# 打印:[36, 37, 94, 38, 64, 33]

列表推導原則:只用列表推導來創建新的列表,並儘量保持簡短
列表推導中的變量泄露問題在Python3中得到了解決,代碼如下

x = 'ABC'
dummy = [ord(x) for x in x]
print(x) 
# 打印:'ABC'
print(dummy) 
# 打印:[65, 66, 67]
# x的值被保留了。列表推導也正確創建了列表。在[ord(x) for x in x]中
# x的作用域被限制在了[]中

filter和map能做的事情 列表推導也可以做

symbols = '$¢£¥€¤'

# 列表推導。遍歷symbols中的每一個字符,如果某一字符的ascii碼 > 127
# 則將其添加進列表中
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
print(beyond_ascii)


# 先用map,調用ord將symbols中的每一個字符轉換成ascii碼,返回值是轉換爲ascii碼後的列表
# 然後外層嵌套一個filter傳一個lambda函數,該函數傳一個參數c 返回滿足 c > 127 的 c
# 然後用這個函數過濾掉 用map轉換後的列表symbols 最後用list再轉換成一個列表賦值給beyond_ascii
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
print(beyond_ascii)

# 顯然 在此函數中列表推導表達式可讀性更強

#使用列表推導計算笛卡爾積
# 對於每一種顏色的襯衫,都有三種不同類型的尺碼
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors
						for size in sizes]
print(tshirts)

列表推導的作用只有一個:生成列表

生成器表達式

什麼是生成器?
任何一個帶有yield語句的函數都是生成器,當你直接調用這個函數時,內部的代碼是不會被執行的,只有調用yield裏面的next函數纔會去執行代碼,for循環也就是會自動去調用這個next函數來輸出值。
可以理解爲一個函數被yield中斷了,下載再次調用時繼續從上一次中斷的位置繼續執行代碼並返回值。通俗一點就是 “要一個元素,給一個元素,而不是列表那樣一次性全部生產出來”

生成器表達式則可以用來創建其他任何類型的序列,用生成器表達式來初始化元組、數組或其他序列類型是更好的選擇,這是因爲生成器表達式背後遵守了迭代器協議,可以逐個地產出元素,而不是先建立一個完整的列表,然後再把這個列表傳遞到某個構造函數裏。可以更好的節省內存

# 示例2-5用生成器表達式初始化元組和數組
# 生成器表達式的語法與列表推導的語法類似,不過是將方括號換位圓括號 然後返回的是一個生成器
# 書中有這樣一段文字:如果生成器表達式是一個函數調用過程中的唯一參數,那麼不需要額外再用括號把它
# 圍起來,可能是翻譯的比較不好理解,個人認爲大概意思就是這樣的。
# 先介紹tuple()函數: tuple() 函數將序列轉換爲元組。而內部的生成器表達式:ord(symbol) for symbol in symbols
# 返回的是一個generator,當其作爲tuple的參數時會自動迭代生成一個序列 所以 他這句話就是 當這個生成器表達式生成的generator作爲
# 一個函數的唯一參數時 不需要加括號
# 更通俗點就是 不需要寫成 tp = tuple((ord(symbol) for symbol in symbols))

symbols = '$¢£¥€¤'

print(type(ord(symbol) for symbol in symbols))
# 輸出:<class 'generator'>
tp = tuple(ord(symbol) for symbol in symbols)
print(tp)
# 輸出:(36, 162, 163, 165, 8364, 164)

import array

# array模塊是python中實現的一種高效的數組存儲類型。
# 它和list相似,但是所有的數組成員必須是同一種類型,在創建數組的時候,就確定了數組的類型

# array.array的構造方法需要兩個參數 因此需要加括號。
# array.array(typecode,[initializer]) --typecode:元素類型代碼指定數字的存儲方式;
# initializer:初始化器,若數組爲空,則省略初始化器
# 其中第一個I代表的是 元素類型爲無符號整型 即unsigned int 而 i 代表有符號整型即signed int
ary = array.array('I', (ord(symbol) for symbol in symbols))

print(ary)

# 示例2-6生成器表達式計算笛卡爾積
colors = ['red', 'black']

sizes = ['S', 'M', 'L']

tshirts = ('%s %s' % (c, s) for c in colors
		   for s in sizes)

print(tshirts)
# 這裏產生的不是一個列表而是一個生成器
# 打印<generator object <genexpr> at 0x0000028CF46B2DE0>

for tshirt in tshirts:
	print(tshirt)

總結:這一小節充分介紹什麼是列表推導,什麼是生成器表達式。介紹了他們的作用與優勢,但並沒有清楚的解釋生成器這一概念。可以參考其他大佬所寫的有關生成器與迭代器的博客,我的理解也比較淺顯。需要更進一步的學習。

發佈了35 篇原創文章 · 獲贊 6 · 訪問量 2632
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章