流畅的Python(Fluent Python)
PART I. Prologue
Chapter 1.?The Python Data Model
1.1 A Pythonic Card Deck
介紹了特殊方法,也叫做魔術(shù)方法
import collectionsCard = collections.namedtuple('Card', ['rank', 'suit'])class FrenchDeck:ranks = [str(n) for n in range(2, 11)] + list('JQKA')suits = 'spades diamonds clubs hearts'.split()def __init__(self):self._cards = [Card(rank, suit) for suit in self.suitsfor rank in self.ranks]def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position] #初始化這個紙牌(初始化一張紙牌的操作) beer_card = Card('7','diamonds')#初始化法國紙牌組 deck = FrenchDeck() 可以直接調(diào)用len(deck),deck[0],choice(deck),for card in deck, Card('Q','hearts') in deck, sorted(deck)等操作,因為實現(xiàn)了特殊方法__len__, __getitem__1.2 How Special Methods Are Used
特殊方法的存在是為了被Python解釋器調(diào)用,你自己并不需要調(diào)用它們。也就是沒有my_object.__len__()這種寫法。自己實現(xiàn)的類用len(my_object),他會自己去調(diào)用__len__方法。Python內(nèi)置的類型,那么 CPython 會抄個近路,__len__ 實際 上會直接返回 PyVarObject 里的 ob_size 屬性。PyVarObject 是表示 內(nèi)存中長度可變的內(nèi)置對象的 C 語言結(jié)構(gòu)體。
for i in x: 這個語句, 背后其實用的是 iter(x),而這個函數(shù)的背后則是 x.__iter__() 方 法。
from math import hypotclass Vector:def __init__(self, x=0, y=0):self.x = xself.y = ydef __repr__(self): #直接在print(Vector)輸出return 'Vector(%r, %r)' % (self.x, self.y)def __abs__(self):return hypot(self.x, self.y)def __bool__(self):return bool(abs(self))def __add__(self, other): #調(diào)用+的時候使用x = self.x + other.xy = self.y + other.yreturn Vector(x, y)def __mul__(self, scalar): #調(diào)用*的時候使用,注意順序return Vector(self.x * scalar, self.y * scalar)#__repr__ 和 __str__ 的區(qū)別在于,后者是在 str() 函數(shù)被使用,或是在用 print 函數(shù)打印一個對象的#時候才被調(diào)用的,并且它返回的字符串對終端用戶更友好。如果你只想實現(xiàn)這兩個特殊方法中的一個,#__repr__ 是更好的選擇,因為如果一個對象沒有 __str__ 函數(shù),而 Python 又需要調(diào)用它的時候,解釋#器會用 __repr__ 作為替代。#默認(rèn)情況下,我們自己定義的類的實例總被認(rèn)為是真的,除非這個類對__bool__ 或者 __len__ 函數(shù)有自 #己的實現(xiàn)。bool(x) 的背后是調(diào)用x.__bool__() 的結(jié)果;如果不存在 __bool__ 方法,那么 bool(x) #會嘗試調(diào)用 x.__len__()。若返回 0,則 bool 會返回 False;否則返回True。1.3 Overview of Special Methods
Python大概有83個特殊方法,其中47個用于實現(xiàn)算術(shù)運算、位運算和比較操作。(其中包括反向運算符和增量賦值運算符a*=b)
1.4 Why len Is Not a Method
因為當(dāng)len(x)中x是一個內(nèi)置類型的實例,那么CPython就會直接從一個C結(jié)構(gòu)體里讀取對象的長度,不會調(diào)用任何方法,非常高效。
1.5 Chapter Summary
通過實現(xiàn)特殊方法,自定義數(shù)據(jù)類型可以表現(xiàn)得跟內(nèi)置類型一樣,從而 讓我們寫出更具表達力的代碼——或者說,更具 Python 風(fēng)格的代碼。
PART II. Data Structures
Chapter 2. An Array of Sequences
2.1 Overview of Built-In Sequences
Python標(biāo)準(zhǔn)庫用C實現(xiàn)了豐富的序列類型:
容器序列(可存放不同類型的數(shù)據(jù)):list, tuple, collections.deque
扁平序列(只能容納一種類型):str, bytes, bytearray, memoryview, array.array
容器序列存放的是它們所包含的任意類型的對象的引用,扁平序列里存放的是值而不是引用(扁平序列是一段連續(xù)的內(nèi)存空間)
可變序列:list, bytearray, array.array, collections.deque和memoryview
不可變序列:tuple, str, bytes
2.2 List Comprehensions and Generator Expressions
列表推導(dǎo)是構(gòu)建列表的快捷方式,而生成器表達式則可以用來創(chuàng)建其它任何類型的序列。
# 把一個字符串變成Unicode碼位的列表 symbols = '$¢£¥€¤' codes = [] for symbol in symbols:codes.append(ord(symbol))# 或者 symbols = '$¢£¥€¤' codes = [ord(symbol) for symbol in symbols]Python 會忽略代碼里 []、{} 和 () 中的換行,因此如果你的代碼里 有多行的列表、列表推導(dǎo)、生成器表達式、字典這一類的,可以省 略不太好看的續(xù)行符 \。
列表推導(dǎo)同filter和map的比較
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))ASCII:'0' : 48, 'A' :?65, 'a' : 97
使用列表推導(dǎo)計算笛卡爾積:
colors = ['black', 'white'] sizes = ['S', 'M', 'L'] tshirts = [(color, size) for size in sizesfor color in colors]列表推導(dǎo)的作用只有一個:生成列表。如果想生成其他類型的序列,生成器表達式就派上了用場。
雖然也可以用列表推導(dǎo)來初始化元組、數(shù)組或其他序列類型,但是生成 器表達式是更好的選擇。這是因為生成器表達式背后遵守了迭代器協(xié) 議,可以逐個地產(chǎn)出元素,而不是先建立一個完整的列表,然后再把這 個列表傳遞到某個構(gòu)造函數(shù)里。前面那種方式顯然能夠節(jié)省內(nèi)存。
生成器表達式的語法跟列表推導(dǎo)差不多,只不過把方括號換成圓括號而 已。
# 用生成器表達式建立元組和數(shù)組symbols = '$¢£¥€¤'tuple(ord(symbol) for symbol in symbols)import arrayarray.array('I', (ord(symbol) for symbol in symbols))# 使用生成器表達式計算笛卡爾積colors = ['black', 'white']sizes = ['S', 'M', 'L']for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):print(tshirt)2.3 Tuples Are Not Just Immutable Lists
2.3.1 Tuples as Records
2.3.2 Tuple Unpacking
用*運算符將一個可迭代對象拆開作為函數(shù)的參數(shù):
>>> t = (20, 8) >>> divmod(*t)# 用*來處理剩下的元素 # 在 Python 中,函數(shù)用 *args 來獲取不確定數(shù)量的參數(shù)算是一種經(jīng)典寫法了 >>> 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, [])# 在平行賦值中,* 前綴只能用在一個變量名前面,但是這個變量可以出 # 現(xiàn)在賦值表達式的任意位置: >>> 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 Nested Tuple Unpacking
2.3.4 NamedTuples
collections.namedtuple 是一個工廠函數(shù),它可以用來構(gòu)建一個帶 字段名的元組和一個有名字的類(這個在acwing算法學(xué)習(xí)里寫過了)
2.3.5 Tuples as Immutable Lists
2.4 Slice Objects
2.4.1 為什么切片和區(qū)間會忽略最后一個元素(幾個小優(yōu)點)
2.4.2 對對象進行切片
>>> s = 'bicycle' >>> s[::3] 'bye' >>> s[::-1] 'elcycib' >>> s[::-2] 'eccb'>>> a = '123456789' >>> slice1 = slice(0,3) >>> slice2 = slice(4,5) >>> a[slice1] >>> a[slice2]2.4.3 Multidimensinal Slicing and Ellipsis
[] 運算符里還可以使用以逗號分開的多個索引或者是切片,外部庫 NumPy 里就用到了這個特性,二維的 numpy.ndarray 就可以用 a[i, j] 這種形式來獲取,抑或是用 a[m:n, k:l] 的方式來得到二維切片。要正確處理這種 [] 運算符的話,對 象的特殊方法 __getitem__ 和 __setitem__ 需要以元組的形式來接收 a[i, j] 中的索引。也就是說,如果要得到 a[i, j] 的值,Python 會 調(diào)用 a.__getitem__((i, j))。
省略(ellipsis)的正確書寫方法是三個英語句號(...),省略在 Python 解析器眼 里是一個符號,而實際上它是 Ellipsis 對象的別名,而 Ellipsis 對象,又是 ellipsis 類的單一實例。 它可以當(dāng)作切片規(guī)范的一部分,也可以用在函數(shù)的參數(shù)清單中,如果 x 是四維數(shù)組,那 么 x[i, ...] 就是 x[i, :, :, :] 的縮寫。
2.4.4 Assigning to Slices
>>> l = list(range(10)) >>> del l[5:7] >>> l[3::2] = [11, 22] >>> l[2:5] = 100 ? Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only assign an iterable >>> l[2:5] = [100] # 如果賦值的對象是一個切片,那么賦值語句的右側(cè)必須是個可迭代 # 對象。即便只有單獨一個值,也要把它轉(zhuǎn)換成可迭代的序列。2.5 Augmented Assignment with Sequences
Python 會新建一個包含同樣類型數(shù)據(jù)的序列來作為拼接的結(jié)果(如列表的+),+ 和 * 都遵循這個規(guī)律,不修改原有的操作對象,而是構(gòu)建一個全新的序列。
追加同一個行對象(row)3 次到游戲板(board)。
>>> board = [] >>> for i in range(3): ... row=['_'] * 3 # ? ... board.append(row)2.6?A += Assignment Puzzler
增量賦值運算符 += 和 *= 的表現(xiàn)取決于它們的第一個操作對象,+= 背后的特殊方法是 __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。也就是說,在這個表達式中, 變量名會不會被關(guān)聯(lián)到新的對象,完全取決于這個類型有沒有實現(xiàn) __iadd__ 這個方法。
對不可變序列進行重復(fù)拼接操作的話,效率會很低,因為每次都有一個 新對象,而解釋器需要把原來對象中的元素先復(fù)制到新的對象里,然后 再追加新的元素。 str 是一個例外,因為對字符串做 += 實在是太普遍了,所以 CPython 對它做了優(yōu)化。為 str 初始化內(nèi)存的時候,程序會為它留出額外的可擴展空間,因此進行增量操作的時候,并不會涉 及復(fù)制原有字符串到新位置這類操作。
1 不要把可變對象放在元組里面。
2 增量賦值不是一個原子操作。我們剛才也看到了,它雖然拋出了異常,但還是完成了操作。
3 查看Python 的字節(jié)碼并不難,而且它對我們了解代碼背后的運行機 制很有幫助。
2.7 list.sort and the sorted Built-In Function
如果一個函數(shù) 或者方法對對象進行的是就地改動,那它就應(yīng)該返回 None。
與 list.sort 相反的是內(nèi)置函數(shù) sorted,它會新建一個列表作為返回 值。這個方法可以接受任何形式的可迭代對象作為參數(shù),甚至包括不可變序列或生成器。而不管 sorted 接受的是怎樣的參數(shù),它最后都會返回一個列表。(都有兩個參數(shù),一個reverse和key,一個只有一個參數(shù)的函數(shù),這個函數(shù)會被用在序列里的每一個元素 上,所產(chǎn)生的結(jié)果將是排序算法依賴的對比關(guān)鍵字。可選參數(shù) key 還可以在內(nèi)置函數(shù) min() 和 max() 中起作用。 另外,還有些標(biāo)準(zhǔn)庫里的函數(shù)也接受這個參數(shù),像 itertools.groupby() 和 heapq.nlargest() 等。
>>> sorted(fruits, key=len)
['grape', 'apple', 'banana', 'raspberry'])
2.8 Managing Ordered Sequences with bisect
bisect 模塊包含兩個主要函數(shù),bisect 和 insort,兩個函數(shù)都利用二分查找算法來在有序序列中查找或插入元素。
2.8.1 Searching with bisect
你可以先用 bisect(haystack, needle) 查找位置 index,再 用 haystack.insert(index, needle) 來插入新值。但你也可用 insort 來一步到位,并且后者的速度更快一些。(插入之后還是有序序列)
?把函數(shù)對象作為一個參數(shù)傳遞后進行調(diào)用。
bisect_left 返回的插入 位置是原序列中跟被插入元素相等的元素的位置,也就是新元素會被放 置于它相等的元素的前面,而 bisect_right 返回的則是跟它相等的元 素之后的位置。
bisect 可以用來建立一個用數(shù)字作為索引的查詢表格,比如說把分?jǐn)?shù)和成績對應(yīng)起來。
>>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): ... i = bisect.bisect(breakpoints, score) ... return grades[i] ... >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] ['F', 'A', 'C', 'C', 'B', 'A', 'A']2.8.2 Inserting with bisect.insort
insort(seq, item) 把變量 item 插入到序列 seq 中,并能保持 seq 的升序順序。
目前所提到的內(nèi)容都不僅僅是對列表或者元組有效,還可以應(yīng)用于幾乎所有的序列類型上。
如果你只需要處 理數(shù)字列表的話,數(shù)組可能是個更好的選擇。下面就來討論一些可以替換列表的數(shù)據(jù)結(jié)構(gòu)。
2.9 When a List Is Not the Answer
要存放 1000 萬個浮點數(shù)的話,數(shù)組(array)的效率要高得多,因為數(shù)組在背后存的并不是float 對象,而是數(shù)字的機器翻譯,也就是字節(jié)表述。這一點就跟 C 語言中的數(shù)組一樣。再比如說,如 果需要頻繁對序列做先進先出的操作,deque(雙端隊列)的速度應(yīng)該會更快。
2.9.1 Arrays
如果我們需要一個只包含數(shù)字的列表,那么array.array 比 list 更 高效。數(shù)組支持所有跟可變序列有關(guān)的操作,包括.pop、.insert 和 .extend。另外,數(shù)組還提供從文件讀取和存入文件的更快的方法,如 .frombytes 和 .tofile。
創(chuàng)建數(shù)組需要一個類型碼,這個類 型碼用來表示在底層的 C 語言應(yīng)該存放怎樣的數(shù)據(jù)類型。
一個浮點型數(shù)組的創(chuàng)建、存入文件和從文件讀取的過程
>>> from array import array ? >>> from random import random >>> floats = array('d', (random() for i in range(10**7))) ? >>> floats[-1] ? 0.07802343889111107 >>> 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() >>> floats2[-1] ? 0.07802343889111107 >>> floats2 == floats ? True?用 array.fromfile 從一個二進制文件里讀出 1000 萬個雙精度浮點數(shù)只需要 0.1 秒,這比從文本文件里讀取的速度要快 60 倍,因為后者會使用內(nèi)置的float 方法把每一行文字轉(zhuǎn)換成浮點數(shù)。
使用 array.tofile 寫入到二進制文件,比以每行一個浮點數(shù)的 方式把所有數(shù)字寫入到文本文件要快 7 倍。
另外一個快速序列化數(shù)字類型的方法是使用 pickle,pickle.dump 處理浮點數(shù)組的速度幾乎跟 array.tofile 一 樣快。不過前者可以處理幾乎所有的內(nèi)置數(shù)字類型,包含復(fù)數(shù)、嵌 套集合,甚至用戶自定義的類。前提是這些類沒有什么特別復(fù)雜的實現(xiàn)。
要給數(shù)組排序的話,得用 sorted 函數(shù)新建一個數(shù)組
a = array.array(a.typecode, sorted(a))2.9.2 Memory Views
memoryview 是一個內(nèi)置類,它能讓用戶在不復(fù)制內(nèi)容的情況下操作同一個數(shù)組的不同切片。memoryview是泛化和去數(shù)學(xué)化的 NumPy 數(shù)組。它讓你在不需要復(fù)制內(nèi)容的前提下,在數(shù)據(jù)結(jié)構(gòu)之間共享內(nèi)存。
memoryview.cast 的概念跟數(shù)組模塊類似,能用不同的方式讀寫同一 塊內(nèi)存數(shù)據(jù),而且內(nèi)容字節(jié)不會隨意移動。這聽上去又跟 C 語言中類型 轉(zhuǎn)換的概念差不多。memoryview.cast 會把同一塊內(nèi)存里的內(nèi)容打包成一個全新的 memoryview 對象給你。
通過改變數(shù)組中的一個字節(jié)來更新數(shù)組里某個元素的值
>>> 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 and SciPy
憑借著 NumPy 和 SciPy 提供的高階數(shù)組和矩陣操作,Python 成為科學(xué)計算應(yīng)用的主流語言。
SciPy 是基于 NumPy 的另一個庫,它提供了很多跟科學(xué)計算有關(guān)的算法,專為線性代數(shù)、數(shù)值積分和統(tǒng)計學(xué)而設(shè)計。
>>> import numpy ? >>> a = numpy.arange(12) ? >>> a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> type(a) <class 'numpy.ndarray'> >>> a.shape ? (12,) >>> a.shape = 3, 4 ? >>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> a[2] ? array([ 8, 9, 10, 11]) >>> a[2, 1] ? 9 >>> a[:, 1] ? array([1, 5, 9]) >>> a.transpose() ? array([[ 0, 4, 8],[ 1, 5, 9],[ 2, 6, 10],[ 3, 7, 11]]) >>> import numpy >>> floats = numpy.loadtxt('floats-10M-lines.txt') ? >>> floats[-3:] ? array([ 3016362.69195522, 535281.10514262, 4566560.44373946]) >>> floats *= .5 ? >>> floats[-3:] array([ 1508181.34597761, 267640.55257131, 2283280.22186973]) >>> from time import perf_counter as pc ? >>> t0 = pc(); floats /= 3; pc() - t0 ? 0.03690556302899495 >>> numpy.save('floats-10M', floats) ? >>> floats2 = numpy.load('floats-10M.npy', 'r+') ? >>> floats2 *= 6 >>> floats2[-3:] ? memmap([3016362.69195522, 535281.10514262, 4566560.44373946])? 把每個元素都除以 3,可以看到處理 1000 萬個浮點數(shù)所需的時間還不足 40 毫秒。
Pandas和Blaze數(shù)據(jù)分析庫就以它們?yōu)榛A(chǔ),提供了高效 的且能存儲非數(shù)值類數(shù)據(jù)的數(shù)組類型,和讀寫常見數(shù)據(jù)文件格式(例如 csv、xls、SQL轉(zhuǎn)儲和 HDF5)的功能。
2.9.4 Deques and Other Queues
collections.deque 類(雙向隊列)是一個線程安全、可以快速從兩 端添加或者刪除元素的數(shù)據(jù)類型。而且如果想要有一種數(shù)據(jù)類型來存 放“最近用到的幾個元素”,deque 也是一個很好的選擇。這是因為在新 建一個雙向隊列的時候,你可以指定這個隊列的大小,如果這個隊列滿 員了,還可以從反向端刪除過期的元素,然后在尾端添加新的元素。
>>> 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.extendleft([10, 20, 30, 40]) ? >>> dq deque([40, 30, 20, 10, 7, 8, 9, 0, 1, 2], maxlen=10)雙向隊列中間刪除元素的操作會慢一些,因為它只對在頭尾的操作進行了優(yōu)化。append 和 popleft 都是原子操作,也就說是 deque 可以在多線程程序 中安全地當(dāng)作先進先出的棧使用,而使用者不需要擔(dān)心資源鎖的問題。
# a_list.pop(p) 這個操作只能用于列表,雙向隊列的這個方法不接收參數(shù)。
queue
提供了同步(線程安全)類 Queue、LifoQueue 和 PriorityQueue,不同的線程可以利用這些數(shù)據(jù)類型來交換信息。這三 個類的構(gòu)造方法都有一個可選參數(shù) maxsize,它接收正整數(shù)作為輸入 值,用來限定隊列的大小。但是在滿員的時候,這些類不會扔掉舊的元 素來騰出位置。相反,如果隊列滿了,它就會被鎖住,直到另外的線程 移除了某個元素而騰出了位置。這一特性讓這些類很適合用來控制活躍 線程的數(shù)量。
multiprocessing
這個包實現(xiàn)了自己的 Queue,它跟 queue.Queue 類似,是設(shè)計給 進程間通信用的。同時還有一個專門的 multiprocessing.JoinableQueue 類型,可以讓任務(wù)管理變得更方便。
asyncio
里面有 Queue、LifoQueue、PriorityQueue 和 JoinableQueue,這些類受 到 queue 和 multiprocessing 模塊的影響,但是為異步編程里的任務(wù) 管理提供了專門的便利。
CHAPTER 3
Dictionaries and Sets
3.1 Generic Mapping Types
collections.abc 模塊中有 Mapping 和 MutableMapping 這兩個抽象 基類,它們的作用是為 dict 和其他類似的類型定義形式接口。
# 用 isinstance 而不是 type 來檢查某個參數(shù)是否為 dict 類型, # 因為這個參數(shù)有可能不是 dict,而是一個比較另類的映射類型>>> my_dict = {} >>> isinstance(my_dict, abc.Mapping) True標(biāo)準(zhǔn)庫里的所有映射類型都是利用 dict 來實現(xiàn)的,因此它們有個共同的限制,即只有可散列的數(shù)據(jù)類型才能用作這些映射里的鍵。(如果一個對象是可散列的,那么在這個對象的生命周期中,它 的散列值是不變的,而且這個對象需要實現(xiàn) __hash__() 方 法。另外可散列對象還要有 __qe__() 方法,這樣才能跟其他鍵做比較。如果兩個可散列對象是相等的,那么它們的散列值一定是一樣的)
原子不可變數(shù)據(jù)類型(str、bytes 和數(shù)值類型)都是可散列類 型,frozenset 也是可散列的,因為根據(jù)其定義,frozenset 里 只能容納可散列類型。元組的話,只有當(dāng)一個元組包含的所有元素 都是可散列類型的情況下,它才是可散列的。
一般來講用戶自定義的類型的對象都是可散列的,散列值就是它們 的 id() 函數(shù)的返回值,所以所有這些對象在比較的時候都是不相 等的。如果一個對象實現(xiàn)了 __eq__ 方法,并且在方法中用到了這 個對象的內(nèi)部狀態(tài)的話,那么只有當(dāng)所有這些內(nèi)部狀態(tài)都是不可變 的情況下,這個對象才是可散列的。
>>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3])) >>> d = dict([('two', 2), ('one', 1), ('three', 3)]) >>> e = dict({'three': 3, 'one': 1, 'two': 2}) >>> a == b == c == d == e True3.2 dict Comprehensions
字典推導(dǎo) (dictcomp)可以從任何以鍵值對作為元素的可迭代對象中構(gòu)建出字 典。
>>> DIAL_CODES = [ ? ... (86, 'China'), ... (91, 'India'), ... (1, 'United States'), ... (62, 'Indonesia'), ... (55, 'Brazil'), ... (92, 'Pakistan'), ... (880, 'Bangladesh'), ... (234, 'Nigeria'), ... (7, 'Russia'), ... (81, 'Japan'), ... ] >>> country_code = {country: code for code, country in DIAL_CODES} ? >>> country_code {'China': 86, 'India': 91, 'Bangladesh': 880, 'United States': 1, 'Pakistan': 92, 'Japan': 81, 'Russia': 7, 'Brazil': 55, 'Nigeria': 234, 'Indonesia': 62} >>> {code: country.upper() for country, code in country_code.items() ? ... if code < 66} {1: 'UNITED STATES', 55: 'BRAZIL', 62: 'INDONESIA', 7: 'RUSSIA'}3.3 Overview of Common Mapping Methods
# OrderedDict.popitem() 會移除字典里最先插入的元素(先進先出);同時這個方法還有一 個可選的 last 參數(shù),若為真,則會移除最后插入的元素(后進先出)。
你既可以用一個映射對象來新建一個映射對象,也可以用包含 (key, value) 元素的可迭代對象來初始化一個 映射對象。
# 整個代碼的功能是統(tǒng)計文本中哪些單詞在哪些地方出現(xiàn)過 # 如a.txt文檔,第一行為 I am a student 第二行為 you are a student too. # 則結(jié)果(按大寫字母順序排序) I [(1,1)] ... student [(1,8),(2,11)]# \w指匹配[A-Za-z0-9_],+匹配一個至多個 # .finditer返回的是一個可迭代對象,然后用group得到被匹配的所有組 # enumerate,為可迭代對象編號import sys import re WORD_RE = re.compile(r'\w+') index = {} with open(sys.argv[1], encoding='utf-8') as fp:for line_no, line in enumerate(fp, 1):for match in WORD_RE.finditer(line):word = match.group()if word=='better':print()column_no = match.start()+1location = (line_no, column_no)# 這其實是一種很不好的實現(xiàn),這樣寫只是為了證明論點# occurrences = index.get(word, [])# occurrences.append(location)# index[word] = occurrences# setdefault可以完成兩個操作 1.得到 key=word的value,如果不存在就# 返回[] 2.向里面插入值index.setdefault(word, []).append(location)# 以字母順序打印出結(jié)果 for word in sorted(index, key=str.upper):print(word, index[word])總結(jié)
以上是生活随笔為你收集整理的流畅的Python(Fluent Python)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2-10 就业课(2.0)-oozie:
- 下一篇: rbac 权限分配, 基于formset