日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

流畅的Python(Fluent Python)

發布時間:2023/12/20 python 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 流畅的Python(Fluent Python) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PART I. Prologue

Chapter 1.?The Python Data Model

1.1 A Pythonic Card Deck

介紹了特殊方法,也叫做魔術方法

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() 可以直接調用len(deck),deck[0],choice(deck),for card in deck, Card('Q','hearts') in deck, sorted(deck)等操作,因為實現了特殊方法__len__, __getitem__

1.2 How Special Methods Are Used

特殊方法的存在是為了被Python解釋器調用,你自己并不需要調用它們。也就是沒有my_object.__len__()這種寫法。自己實現的類用len(my_object),他會自己去調用__len__方法。Python內置的類型,那么 CPython 會抄個近路,__len__ 實際 上會直接返回 PyVarObject 里的 ob_size 屬性。PyVarObject 是表示 內存中長度可變的內置對象的 C 語言結構體。

for i in x: 這個語句, 背后其實用的是 iter(x),而這個函數的背后則是 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): #調用+的時候使用x = self.x + other.xy = self.y + other.yreturn Vector(x, y)def __mul__(self, scalar): #調用*的時候使用,注意順序return Vector(self.x * scalar, self.y * scalar)#__repr__ 和 __str__ 的區別在于,后者是在 str() 函數被使用,或是在用 print 函數打印一個對象的#時候才被調用的,并且它返回的字符串對終端用戶更友好。如果你只想實現這兩個特殊方法中的一個,#__repr__ 是更好的選擇,因為如果一個對象沒有 __str__ 函數,而 Python 又需要調用它的時候,解釋#器會用 __repr__ 作為替代。#默認情況下,我們自己定義的類的實例總被認為是真的,除非這個類對__bool__ 或者 __len__ 函數有自 #己的實現。bool(x) 的背后是調用x.__bool__() 的結果;如果不存在 __bool__ 方法,那么 bool(x) #會嘗試調用 x.__len__()。若返回 0,則 bool 會返回 False;否則返回True。

1.3 Overview of Special Methods

Python大概有83個特殊方法,其中47個用于實現算術運算、位運算和比較操作。(其中包括反向運算符和增量賦值運算符a*=b)

1.4 Why len Is Not a Method

因為當len(x)中x是一個內置類型的實例,那么CPython就會直接從一個C結構體里讀取對象的長度,不會調用任何方法,非常高效。

1.5 Chapter Summary

通過實現特殊方法,自定義數據類型可以表現得跟內置類型一樣,從而 讓我們寫出更具表達力的代碼——或者說,更具 Python 風格的代碼。

PART II. Data Structures

Chapter 2. An Array of Sequences

2.1 Overview of Built-In Sequences

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 Comprehensions and Generator Expressions

列表推導是構建列表的快捷方式,而生成器表達式則可以用來創建其它任何類型的序列。

# 把一個字符串變成Unicode碼位的列表 symbols = '$¢£¥€¤' codes = [] for symbol in symbols:codes.append(ord(symbol))# 或者 symbols = '$¢£¥€¤' codes = [ord(symbol) for symbol in symbols]

Python 會忽略代碼里 []、{} 和 () 中的換行,因此如果你的代碼里 有多行的列表、列表推導、生成器表達式、字典這一類的,可以省 略不太好看的續行符 \。

列表推導同filter和map的比較

beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))

ASCII:'0' : 48, 'A' :?65, 'a' : 97

使用列表推導計算笛卡爾積:

colors = ['black', 'white'] sizes = ['S', 'M', 'L'] tshirts = [(color, size) for size in sizesfor color in colors]

列表推導的作用只有一個:生成列表。如果想生成其他類型的序列,生成器表達式就派上了用場。

雖然也可以用列表推導來初始化元組、數組或其他序列類型,但是生成 器表達式是更好的選擇。這是因為生成器表達式背后遵守了迭代器協 議,可以逐個地產出元素,而不是先建立一個完整的列表,然后再把這 個列表傳遞到某個構造函數里。前面那種方式顯然能夠節省內存。

生成器表達式的語法跟列表推導差不多,只不過把方括號換成圓括號而 已。

# 用生成器表達式建立元組和數組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

用*運算符將一個可迭代對象拆開作為函數的參數:

>>> t = (20, 8) >>> divmod(*t)# 用*來處理剩下的元素 # 在 Python 中,函數用 *args 來獲取不確定數量的參數算是一種經典寫法了 >>> 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 Nested Tuple Unpacking

2.3.4 NamedTuples

collections.namedtuple 是一個工廠函數,它可以用來構建一個帶 字段名的元組和一個有名字的類(這個在acwing算法學習里寫過了)

2.3.5 Tuples as Immutable Lists

2.4 Slice Objects

2.4.1 為什么切片和區間會忽略最后一個元素(幾個小優點)

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 會 調用 a.__getitem__((i, j))。

省略(ellipsis)的正確書寫方法是三個英語句號(...),省略在 Python 解析器眼 里是一個符號,而實際上它是 Ellipsis 對象的別名,而 Ellipsis 對象,又是 ellipsis 類的單一實例。 它可以當作切片規范的一部分,也可以用在函數的參數清單中,如果 x 是四維數組,那 么 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] # 如果賦值的對象是一個切片,那么賦值語句的右側必須是個可迭代 # 對象。即便只有單獨一個值,也要把它轉換成可迭代的序列。

2.5 Augmented Assignment with Sequences

Python 會新建一個包含同樣類型數據的序列來作為拼接的結果(如列表的+),+ 和 * 都遵循這個規律,不修改原有的操作對象,而是構建一個全新的序列。

追加同一個行對象(row)3 次到游戲板(board)。

>>> board = [] >>> for i in range(3): ... row=['_'] * 3 # ? ... board.append(row)

2.6?A += Assignment Puzzler

增量賦值運算符 += 和 *= 的表現取決于它們的第一個操作對象,+= 背后的特殊方法是 __iadd__ (用于“就地加法”)。但是如果一個類 沒有實現這個方法的話,Python 會退一步調用 __add__ 。

如果 a 實現了 __iadd__ 方法,就會調用這個方法。同時對可變序列 (例如 list、bytearray 和 array.array)來說,a 會就地改動,就 像調用了 a.extend(b) 一樣。但是如果 a 沒有實現 __iadd__ 的話,a += b 這個表達式的效果就變得跟 a = a + b 一樣了:首先計算 a + b,得到一個新的對象,然后賦值給 a。也就是說,在這個表達式中, 變量名會不會被關聯到新的對象,完全取決于這個類型有沒有實現 __iadd__ 這個方法。

對不可變序列進行重復拼接操作的話,效率會很低,因為每次都有一個 新對象,而解釋器需要把原來對象中的元素先復制到新的對象里,然后 再追加新的元素。 str 是一個例外,因為對字符串做 += 實在是太普遍了,所以 CPython 對它做了優化。為 str 初始化內存的時候,程序會為它留出額外的可擴展空間,因此進行增量操作的時候,并不會涉 及復制原有字符串到新位置這類操作。

1 不要把可變對象放在元組里面。

2 增量賦值不是一個原子操作。我們剛才也看到了,它雖然拋出了異常,但還是完成了操作。

3 查看Python 的字節碼并不難,而且它對我們了解代碼背后的運行機 制很有幫助。

2.7 list.sort and the sorted Built-In Function

如果一個函數 或者方法對對象進行的是就地改動,那它就應該返回 None。

與 list.sort 相反的是內置函數 sorted,它會新建一個列表作為返回 值。這個方法可以接受任何形式的可迭代對象作為參數,甚至包括不可變序列或生成器。而不管 sorted 接受的是怎樣的參數,它最后都會返回一個列表。(都有兩個參數,一個reverse和key,一個只有一個參數的函數,這個函數會被用在序列里的每一個元素 上,所產生的結果將是排序算法依賴的對比關鍵字。可選參數 key 還可以在內置函數 min() 和 max() 中起作用。 另外,還有些標準庫里的函數也接受這個參數,像 itertools.groupby() 和 heapq.nlargest() 等。

>>> sorted(fruits, key=len)

['grape', 'apple', 'banana', 'raspberry'])

2.8 Managing Ordered Sequences with bisect

bisect 模塊包含兩個主要函數,bisect 和 insort,兩個函數都利用二分查找算法來在有序序列中查找或插入元素。

2.8.1 Searching with bisect

你可以先用 bisect(haystack, needle) 查找位置 index,再 用 haystack.insert(index, needle) 來插入新值。但你也可用 insort 來一步到位,并且后者的速度更快一些。(插入之后還是有序序列)

?把函數對象作為一個參數傳遞后進行調用。

bisect_left 返回的插入 位置是原序列中跟被插入元素相等的元素的位置,也就是新元素會被放 置于它相等的元素的前面,而 bisect_right 返回的則是跟它相等的元 素之后的位置。

bisect 可以用來建立一個用數字作為索引的查詢表格,比如說把分數和成績對應起來。

>>> 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 的升序順序。

目前所提到的內容都不僅僅是對列表或者元組有效,還可以應用于幾乎所有的序列類型上。

如果你只需要處 理數字列表的話,數組可能是個更好的選擇。下面就來討論一些可以替換列表的數據結構。

2.9 When a List Is Not the Answer

要存放 1000 萬個浮點數的話,數組(array)的效率要高得多,因為數組在背后存的并不是float 對象,而是數字的機器翻譯,也就是字節表述。這一點就跟 C 語言中的數組一樣。再比如說,如 果需要頻繁對序列做先進先出的操作,deque(雙端隊列)的速度應該會更快。

2.9.1 Arrays

如果我們需要一個只包含數字的列表,那么array.array 比 list 更 高效。數組支持所有跟可變序列有關的操作,包括.pop、.insert 和 .extend。另外,數組還提供從文件讀取和存入文件的更快的方法,如 .frombytes 和 .tofile。

創建數組需要一個類型碼,這個類 型碼用來表示在底層的 C 語言應該存放怎樣的數據類型。

一個浮點型數組的創建、存入文件和從文件讀取的過程

>>> 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 萬個雙精度浮點數只需要 0.1 秒,這比從文本文件里讀取的速度要快 60 倍,因為后者會使用內置的float 方法把每一行文字轉換成浮點數。

使用 array.tofile 寫入到二進制文件,比以每行一個浮點數的 方式把所有數字寫入到文本文件要快 7 倍。

另外一個快速序列化數字類型的方法是使用 pickle,pickle.dump 處理浮點數組的速度幾乎跟 array.tofile 一 樣快。不過前者可以處理幾乎所有的內置數字類型,包含復數、嵌 套集合,甚至用戶自定義的類。前提是這些類沒有什么特別復雜的實現。

要給數組排序的話,得用 sorted 函數新建一個數組

a = array.array(a.typecode, sorted(a))

2.9.2 Memory Views

memoryview 是一個內置類,它能讓用戶在不復制內容的情況下操作同一個數組的不同切片。memoryview是泛化和去數學化的 NumPy 數組。它讓你在不需要復制內容的前提下,在數據結構之間共享內存。

memoryview.cast 的概念跟數組模塊類似,能用不同的方式讀寫同一 塊內存數據,而且內容字節不會隨意移動。這聽上去又跟 C 語言中類型 轉換的概念差不多。memoryview.cast 會把同一塊內存里的內容打包成一個全新的 memoryview 對象給你。

通過改變數組中的一個字節來更新數組里某個元素的值

>>> 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 提供的高階數組和矩陣操作,Python 成為科學計算應用的主流語言。

SciPy 是基于 NumPy 的另一個庫,它提供了很多跟科學計算有關的算法,專為線性代數、數值積分和統計學而設計。

>>> 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 萬個浮點數所需的時間還不足 40 毫秒。

Pandas和Blaze數據分析庫就以它們為基礎,提供了高效 的且能存儲非數值類數據的數組類型,和讀寫常見數據文件格式(例如 csv、xls、SQL轉儲和 HDF5)的功能。

2.9.4 Deques and Other Queues

collections.deque 類(雙向隊列)是一個線程安全、可以快速從兩 端添加或者刪除元素的數據類型。而且如果想要有一種數據類型來存 放“最近用到的幾個元素”,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)

雙向隊列中間刪除元素的操作會慢一些,因為它只對在頭尾的操作進行了優化。append 和 popleft 都是原子操作,也就說是 deque 可以在多線程程序 中安全地當作先進先出的棧使用,而使用者不需要擔心資源鎖的問題。

# a_list.pop(p) 這個操作只能用于列表,雙向隊列的這個方法不接收參數。

queue

提供了同步(線程安全)類 Queue、LifoQueue 和 PriorityQueue,不同的線程可以利用這些數據類型來交換信息。這三 個類的構造方法都有一個可選參數 maxsize,它接收正整數作為輸入 值,用來限定隊列的大小。但是在滿員的時候,這些類不會扔掉舊的元 素來騰出位置。相反,如果隊列滿了,它就會被鎖住,直到另外的線程 移除了某個元素而騰出了位置。這一特性讓這些類很適合用來控制活躍 線程的數量。

multiprocessing

這個包實現了自己的 Queue,它跟 queue.Queue 類似,是設計給 進程間通信用的。同時還有一個專門的 multiprocessing.JoinableQueue 類型,可以讓任務管理變得更方便。

asyncio

里面有 Queue、LifoQueue、PriorityQueue 和 JoinableQueue,這些類受 到 queue 和 multiprocessing 模塊的影響,但是為異步編程里的任務 管理提供了專門的便利。

CHAPTER 3

Dictionaries and Sets

3.1 Generic Mapping Types

collections.abc 模塊中有 Mapping 和 MutableMapping 這兩個抽象 基類,它們的作用是為 dict 和其他類似的類型定義形式接口。

# 用 isinstance 而不是 type 來檢查某個參數是否為 dict 類型, # 因為這個參數有可能不是 dict,而是一個比較另類的映射類型>>> my_dict = {} >>> isinstance(my_dict, abc.Mapping) True

標準庫里的所有映射類型都是利用 dict 來實現的,因此它們有個共同的限制,即只有可散列的數據類型才能用作這些映射里的鍵。(如果一個對象是可散列的,那么在這個對象的生命周期中,它 的散列值是不變的,而且這個對象需要實現 __hash__() 方 法。另外可散列對象還要有 __qe__() 方法,這樣才能跟其他鍵做比較。如果兩個可散列對象是相等的,那么它們的散列值一定是一樣的)

原子不可變數據類型(str、bytes 和數值類型)都是可散列類 型,frozenset 也是可散列的,因為根據其定義,frozenset 里 只能容納可散列類型。元組的話,只有當一個元組包含的所有元素 都是可散列類型的情況下,它才是可散列的。

一般來講用戶自定義的類型的對象都是可散列的,散列值就是它們 的 id() 函數的返回值,所以所有這些對象在比較的時候都是不相 等的。如果一個對象實現了 __eq__ 方法,并且在方法中用到了這 個對象的內部狀態的話,那么只有當所有這些內部狀態都是不可變 的情況下,這個對象才是可散列的。

>>> 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 True

3.2 dict Comprehensions

字典推導 (dictcomp)可以從任何以鍵值對作為元素的可迭代對象中構建出字 典。

>>> 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 參數,若為真,則會移除最后插入的元素(后進先出)。

你既可以用一個映射對象來新建一個映射對象,也可以用包含 (key, value) 元素的可迭代對象來初始化一個 映射對象。

# 整個代碼的功能是統計文本中哪些單詞在哪些地方出現過 # 如a.txt文檔,第一行為 I am a student 第二行為 you are a student too. # 則結果(按大寫字母順序排序) 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)# 這其實是一種很不好的實現,這樣寫只是為了證明論點# occurrences = index.get(word, [])# occurrences.append(location)# index[word] = occurrences# setdefault可以完成兩個操作 1.得到 key=word的value,如果不存在就# 返回[] 2.向里面插入值index.setdefault(word, []).append(location)# 以字母順序打印出結果 for word in sorted(index, key=str.upper):print(word, index[word])

總結

以上是生活随笔為你收集整理的流畅的Python(Fluent Python)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。