Python模塊itertools, functools
〇、前言
本系列(指Python Moudules系列)每篇介紹一個或多個內(nèi)容沒有特別多模塊
模塊介紹依賴官方文檔(https://docs.python.org/zh-cn/3.8/library/index.html)或其他第三方官方文檔,主要是對其內(nèi)容的補充和拓展
內(nèi)容大部分來源官方文檔,少部分來源網(wǎng)絡(luò)。太過深奧的內(nèi)容會只寫出不介紹,或省略,或省略掉太過深奧的部分(例如省略部分參數(shù)),或通過補充介紹(補充介紹的前面通常會有參考網(wǎng)址)
注意內(nèi)容非常依賴編號。內(nèi)容中類的展示方式是"編號<數(shù)字>."表示類的構(gòu)造方法(__init__),"編號<數(shù)字>.<數(shù)字>"表示類的方法或?qū)傩?,方法一般是實例方法,其他方法會在前面?zhèn)渥?,屬性一般是實例屬性,類屬性會在后面?zhèn)渥?。如果?gòu)造方法無需傳參會直接展示類名
使用Python版本3.8,但也會補充高版本的修改和添加內(nèi)容。系統(tǒng)Windows 7
系列WPS筆記及其他內(nèi)容的百度網(wǎng)盤鏈接:
https://pan.baidu.com/s/1fTwjyoM81_OOAccPNhGE9Q?pwd=h3fg
顏色代表含義:
淡灰色:注釋,一般前面有#
綠色:示例
橙色:拓展
紫色:示例中的input用戶輸入內(nèi)容
紅色、藍色:突出或裝飾文字作用
部分內(nèi)容可能不嚴謹或者錯誤,歡迎指出

一、itertools——為高效循環(huán)而創(chuàng)建迭代器的函數(shù)
(1)、概念
itertools內(nèi)置模塊實現(xiàn)一系列迭代器,標準化了一個快速、高效利用內(nèi)存的核心工具集,這些工具本身或組合一起形成了“迭代器代數(shù)”,這使得在純Python中有可能創(chuàng)建簡潔又高效的專用工具
這些內(nèi)置工具同時也能很好地與operator模塊中的高效函數(shù)配合使用
(2)、函數(shù)
函數(shù)均創(chuàng)建并返回迭代器。有些迭代器不限制輸出流的長度(例如count()),這些迭代器只應(yīng)在能截斷輸出流的函數(shù)或循環(huán)中使用
# 官方文檔中的“大致相當于:”中代碼不會展示
# 事實上這些函數(shù)都是類的實例化或方法
1.?無窮迭代器
# 無窮指迭代器迭代的元素是無窮的
1.1.?count(start=0, step=1)
創(chuàng)建一個迭代器,它從start值開始,按步長step返回均勻間隔的值
# 當對浮點數(shù)計數(shù)時,替換為乘法代碼有時精度會更好,例如:(start + step * i for i in count())
示例:count(5, 2) --> 5, 7, 9, 11, 13, ...
1.2.?cycle(iterable)
創(chuàng)建一個迭代器,返回iterable中所有元素并保存一個副本。當取完iterable中的元素時,返回副本中的所有元素。無限循環(huán)
# 注意,該函數(shù)可能需要相當大的輔助空間(取決于iterable的長度)
示例:cycle([1, 'A', True, None]) --> 1, A, True, None, 1, A, True, None, ...
1.3.?repeat(object[, times])
創(chuàng)建一個迭代器,重復(fù)times次object。如果times未指定則無限重復(fù)
# 常用在map或zip中提供一個常量流
示例:
repeat(5) --> 5, 5, 5, 5, 5, ...
repeat(5, 5) -> 5, 5, 5, 5, 5
list(map(pow, range(5), repeat(2))) --> [0, 1, 4, 9, 16]
2.?根據(jù)最短輸入序列長度停止的迭代器
2.1.?accumulate(iterable[, func, *, initial=None])
創(chuàng)建一個迭代器,返回iterable中元素的累積匯總值或其他雙目運算函數(shù)的累積結(jié)果值
func——應(yīng)為帶有兩個參數(shù)的函數(shù)(雙目運算函數(shù))。iterable 中的元素可以是任何能被func接受為參數(shù)的任意類型
initial——累加的初始值,這會使輸出比輸入的iterable多一個元素
示例:
accumulate([1, 1, 2, 2, 3]) --> 1, 2, 4, 6, 9
accumulate(['a', 'b', 'c', 'd', 'e']) --> a, ab, abc, abcd, abcde
accumulate([1, 1, 2, 2, 3], initial=100) --> 100, 101, 102, 104, 106, 109
accumulate([1, 1, 2, 2, 3], func=lambda a, b: a*b) --> 1, 1, 2, 4, 12
2.2.?chain(*iterables)
創(chuàng)建一個迭代器,依次返回iterables中的所有元素,直到耗盡??蓪⒍鄠€序列處理為單個序列
示例:
chain('aaa', 'bbb', 'abc') --> a, a, a, b, b, b, a, b, c
chain([1, 2], (3, 4), {5, 6}) --> 1, 2, 3, 4, 5, 6
2.3.?chain.from_iterable(iterable)
創(chuàng)建一個迭代器,依次返回iterable(注意是單一的iterable)中的所有元素,直到耗盡。參數(shù)是延遲計算的
示例:
chain.from_iterable(['a', 'bb', 'ccc']) --> a, b, b, c, c, c
補充:延遲計算/延遲求值/惰性求值(Lazy Evaluation)
# 摘抄自百度百科
表達式不在它被綁定到變量之后就立即求值,而是在該值被取用的時候求值
示例:
2.4.?compress(data, selectors)
創(chuàng)建一個迭代器,返回data中元素對應(yīng)的selectors中元素布爾值為True的元素。迭代器再兩者較短長度處停止
示例:
compress('ABCDEFG', [1, 1, 0, 1, 0, 1, 0]) --> A, B, D, F
compress(count(), cycle([1, 0])?--> 0, 2, 4, 6, ...
2.5.?dropwhile(predicate, iterable)
創(chuàng)建一個迭代器,并將iterable中的元素作為參數(shù)傳遞給predicate進行判斷,在首次判斷為False后返回剩下的元素
示例:
dropwhile(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 6, 1, 0, -100
2.6.?takewhile(predicate, iterable)
創(chuàng)建一個迭代器,并將iterable中的元素作為參數(shù)傳遞給predicate進行判斷,在首次判斷為False后停止迭代
示例:
takewhile(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 1, 2, 3
?
2.7.?filterfalse(predicate, iterable)
創(chuàng)建一個迭代器,并將iterable中的所有元素作為參數(shù)傳遞給predicate進行判斷,只返回判斷結(jié)果是False的元素。如果predicate是None,則返回predicate中布爾值為False的元素
# 相當于內(nèi)置filter()函數(shù)的反向函數(shù)
示例:
filterfalse(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 6
filter(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 1, 2, 3, 1, 0, -100
2.8.?groupby(iterable, key=None)
創(chuàng)建一個迭代器,返回iterable中連續(xù)鍵及對應(yīng)組:(鍵, 組)
key——計算元素鍵值的函數(shù),元素會作為參數(shù)傳遞給此函數(shù)。key默認是恒等函數(shù)(identity function,返回值等于傳入值的函數(shù))
# 一般iterable需要以key作為依據(jù)排序:sorted(iterable, key=key)
# 組本身也是一個迭代器,當groupby()對象向后迭代時,前一個組會消失。因此如果后面需要組,可將它保存為列表
示例:
運行結(jié)果:
[('a', ['a', 'a', 'a']), ('b', ['b']), ('c', ['c', 'c']), ('a', ['a'])]
[0, 1] [[2, 2], [1, 3, 1, 3]]
2.9.?islice(iterable, stop)/islice(iterable, start, stop[, step])
創(chuàng)建一個迭代器,類似于切片地從iterable返回選定的元素
將start, stop, step設(shè)置為None相當在切片中留空
與普通切片不同的是不支持將start, stop, step設(shè)置為負值
示例:
islice('ABCDEFG', 2, None) --> C, D, E, F, G
islice('ABCDEFG', 0, None, 2) --> A, C, E, G
islice(range(0, 100, 3), 0, None, 3) --> 0, 9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99
2.10.?starmap(function, iterable)
創(chuàng)建一個迭代器,類似于內(nèi)置函數(shù)map(),不過iterable中的每一項會被解包再作為函數(shù)參數(shù)
# 例如可迭代對象[(1,2), (3, 4)]會轉(zhuǎn)化為等價于[function(1, 2), function(3, 4)]的調(diào)用
示例:
starmap(lambda a, b: a + b, [(1, 2), (2, 3), (3, 4)]) --> 3, 5, 7
map(lambda a, b: a + b, (1, 2, 3), (2, 3, 4)) --> 3, 5, 7
2.11.?tee(iterable, n=2)
從一個可迭代對象中返回n個獨立的迭代器。調(diào)用tee()創(chuàng)建迭代器后,原有的iterable不應(yīng)再被使用,否則會使tee向后迭代或迭代元素多出創(chuàng)建時的元素數(shù)量
示例:
a, b = tee([1, 2, 3, 4, 5])
next(a) --> 1
list(b) --> [1, 2, 3, 4, 5]
# 迭代器a和b互不影響
2.12.?zip_longest(*iterables, fillvalue=None)
創(chuàng)建一個迭代器,從每個可迭代對象(iterables中的每個元素)中收集元素并將它們組成一個元組返回。如果可迭代對象的長度不相等,將根據(jù)fillvalue填充缺失值。迭代持續(xù)到耗光最長的可迭代對象
# 相當于內(nèi)置函數(shù)zip()以長的為基準的打包
示例:
l1 = ['a', 'b', 'c', 'd', 'e']
l2 = ['1', '2', '3']
?
zip_longest(l1, l2, fillvalue='-')?--> ('a', '1'), ('b', '2'), ('c', '3'), ('d', '-'), ('e', '-')
zip(l1, l2)?--> ('a', '1'), ('b', '2'), ('c', '3')
3.?排列組合迭代器
# 元素值相同位置不同會被認為是不同的
3.1.?product(*iterables, repeat=1)
創(chuàng)建一個迭代器,返回iterables的笛卡兒積
repeat——計算可迭代對象自身的笛卡兒積可將此參數(shù)設(shè)定要重復(fù)的次數(shù)。如product(A, repeat=4)等價于product(A, A, A, A)
# 大致相當于生成器表達式中的嵌套循環(huán)。如product(A, B)和((x,y) for x in A for y in B)返回的結(jié)果相同
示例:
product('AB', 'CD') --> ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D')
product('AB', repeat=2) --> ('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')
product('ab', 'cd', 'ef') --> ('a', 'c', 'e'), ('a', 'c', 'f'), ('a', 'd', 'e'), ('a', 'd', 'f'), ('b', 'c', 'e'), ('b', 'c', 'f'), ('b', 'd', 'e'), ('b', 'd', 'f')
補充:笛卡爾積(Cartesian product)
# 摘編自百度百科
兩個集合X和Y的笛卡兒積,又稱直積,表示為X×Y,是第一元素是X的成員而第二元素是Y成員的所有可能有序?qū)Φ钠渲幸粋€成員
3.2.?permutations(iterable, r=None)
創(chuàng)建一個迭代器,返回由iterable元素生成長度為r的排列,無重復(fù)元素
r——如果r未指定或為None,則r默認設(shè)置為iterable的長度(全排列)
示例:
permutations(['A', 'B']) --> ('A', 'B'), ('B', 'A')
permutations(['A', 'B', 'C'], r=2) --> ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')
3.3.?combinations(iterable, r)
創(chuàng)建一個迭代器,返回由iterable元素生成長度為r的組合,無重復(fù)元素
示例:
combinations(['A', 'B'], r=2) --> ('A', 'B')
combinations(['A', 'B', 'C'], r=2) --> ('A', 'B'), ('A', 'C'), ('B', 'C')
3.4.?combinations_with_replacement(iterable, r)
創(chuàng)建一個迭代器,返回由iterable元素生成長度為r的組合,有重復(fù)元素
示例:
combinations_with_replacement(['A', 'B'], r=2) --> ('A', 'A'), ('A', 'B'), ('B', 'B')
combinations_with_replacement(['A', 'B', 'C'], r=2) --> ('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')
補充:itertools配方
itertools 配方是使用itertools作為基礎(chǔ)構(gòu)件來創(chuàng)建擴展的工具集
可以安裝more-itertools模塊獲取許多itertools配方
二、functools——高階函數(shù)和可調(diào)用對象上的操作
(1)、概念
functools內(nèi)置模塊應(yīng)用于高階函數(shù)(參數(shù)或/和返回值為其他函數(shù)的函數(shù))。通常此模塊的功能適用于所有可調(diào)用對象
# 只會簡述部分函數(shù),具體見官方文檔
(2)、部分函數(shù)/裝飾器
1.?@functools.cached_property(func)
將一個類方法轉(zhuǎn)換為特征屬性并一次性計算該特征屬性的值(實測在獲取屬性的時計算),然后將其緩存為實例生命周期內(nèi)的普通屬性。緩存的值可通過刪除該屬性來清空
# 此裝飾器要求每個實例上的__dict__是可變的映射
示例:
運行結(jié)果:
data_num: 3
cache_data_num: 3
data_num: 6
cache_data_num: 3
cache_data_num: 6
2.?@functools.lru_cache(maxsize=128, typed=False)
為函數(shù)提供緩存功能的裝飾器,緩存maxsize組傳入?yún)?shù),在下次同樣參數(shù)調(diào)用時直接返回同樣的結(jié)果
# 由于使用了字典存儲緩存,所以該函數(shù)的固定參數(shù)和關(guān)鍵字參數(shù)必須是可哈希的
maxsize——緩存多少組傳入?yún)?shù),當值為None時禁用LRU特性且緩存可無限增長
typed——如果為True,則不同類型的參數(shù)會分別緩存(例如f(3)和f(3.0))
?
被裝飾的函數(shù)帶有一個cache_info()函數(shù),調(diào)用該函數(shù)返回一個具名元組,包含hits(命中次數(shù):緩存使用次數(shù)), misses(未命中次數(shù):緩存次數(shù)), maxsize(最大緩存數(shù)量), currsize(當前緩存大小)。在多線程環(huán)境中hits和misses是不完全準確
?
被裝飾的函數(shù)也有一個cache_clear()函數(shù),用于清理/使緩存失效
?
原始的未經(jīng)裝飾的函數(shù)可以通過__wrapped__屬性訪問
?
補充:最近最久未使用算法(LRU)
# 摘抄自:https://zhuanlan.zhihu.com/p/60731274
頁面置換算法的一種。LRU算法根據(jù)數(shù)據(jù)的歷史訪問記錄來進行淘汰數(shù)據(jù)。核心思想是“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高”
示例:
運行結(jié)果: ?# 其中一次運行結(jié)果
1.函數(shù)調(diào)用時間:0.006s
2.函數(shù)調(diào)用時間:0.005s
3.函數(shù)調(diào)用時間:0.004s
4.函數(shù)調(diào)用時間:0.006s
5.函數(shù)調(diào)用時間:0.0s
6.函數(shù)調(diào)用時間:0.0s
7.函數(shù)調(diào)用時間:0.006s
CacheInfo(hits=2, misses=5, maxsize=1, currsize=1)
3.?@functools.total_ordering
裝飾一個包含__lt__(), __le__(), __gt__()或__ge__()比較排序方法,且支持__eq__()方法的類,用來實現(xiàn)剩余的方法
# 雖然此裝飾器使得創(chuàng)建具有良好行為的完全有序類型變得非常容易,但它確實是以執(zhí)行速度更緩慢和派生比較方法的堆棧回溯更復(fù)雜為代價的。如果性能基準測試表明這是特定應(yīng)用的瓶頸所在,則改為實現(xiàn)全部六個富比較方法應(yīng)該會輕松提升速度
示例:
運行結(jié)果:
False False False True
4.?partial(func /, *args, **keywords)
返回一個partial對象,當這個對象被調(diào)用行為類似于func,但會附帶位置參數(shù)args和關(guān)鍵字參數(shù)keywords調(diào)用(像是“凍結(jié)了”func的一部分位置參數(shù)/關(guān)鍵字參數(shù))
partial對象有三個只讀屬性,都是調(diào)用時的傳入?yún)?shù):
func, args, keywors
# partial對象不會自動創(chuàng)建__name__和__doc__屬性,且在類中定義partial行為類似靜態(tài)方法,不會在實例屬性查找期間轉(zhuǎn)換為綁定方法
示例:
運行結(jié)果:
3 1 4
20
5.?partialmethod(func, /, *args, **keywords)
返回一個新的partialmethod描述器,行為類似partial()但被設(shè)計用作方法定義
6.?reduce(function, iterable[, initializer])
將需傳入兩個參數(shù)的function從左至右累積地應(yīng)用到iterable的條目,以便將該可迭代對象縮減為單一的值
initializer——它會被放在參與計算的可迭代對象的條目之前,并在可迭代對象為空時作為默認值
# 類似于itertools.accumulate(),但它只返回一個最終累積值
# 實測initializer不能用關(guān)鍵字傳參
示例:
reduce(lambda x, y: x + y, [1, 2, 3, 4], 100) --> 110
itertools.accumulate([1, 2, 3, 4], lambda x, y: x + y, initial=100) --> [100, 101, 103, 106, 110]
補充:泛型函數(shù)(generic function)
為不同的類型實現(xiàn)相同操作的多個函數(shù)所組成的函數(shù)。在調(diào)用時會由調(diào)度算法來確定應(yīng)該使用哪個實現(xiàn)
補充:單分派和多分派(single dispatch & multi dispatch)
# 參考:https://www.cnblogs.com/wongbingming/p/10726698.html
單分派是根據(jù)一個參數(shù)的類型,以不同方式執(zhí)行相同的操作的行為
多分派是根據(jù)多個參數(shù)的類型選擇專門的函數(shù)的行為
補充:重載(Overload)
# 參考:https://zhuanlan.zhihu.com/p/313572294
重載是在一個類里面,方法名相同,而參數(shù)不同。返回類型可以相同也可以不同
# 重寫(Override)是在子類和父類之間的,方法名,參數(shù)相同。返回類型相同
7.?@functools.singledispatch
將一個函數(shù)轉(zhuǎn)換為單分派泛型函數(shù)
分派作用于第一個參數(shù)的類型。調(diào)用時,泛型函數(shù)根據(jù)第一個參數(shù)的類型分派
使用泛型函數(shù)的register裝飾器將重載的實現(xiàn)添加到函數(shù)中。對于有類型標注的函數(shù),裝飾器會自動推動第一個參數(shù)的類型;對應(yīng)沒有類型標注的函數(shù),可將適當類型參數(shù)顯示傳遞給裝飾器本身
使用函數(shù)形式的register(類型, 函數(shù))可注冊現(xiàn)有函數(shù)和匿名函數(shù)
register()屬性裝飾的函數(shù)未真正被裝飾
使用泛型函數(shù)的dispatch(類型)屬性檢查對應(yīng)類型的實現(xiàn)
使用只讀屬性registry訪問所有已注冊的實現(xiàn)
示例:
運行結(jié)果: ?# 其中一次運行結(jié)果
False False False
<function <lambda> at 0x000000000232C550>
{<class 'object'>: <function func at 0x0000000001E0FEE0>, <class 'int'>: <function func_int at 0x000000000232C4C0>, <class 'float'>: <function func_float at 0x00000000023AF160>, <class 'bool'>: <function <lambda> at 0x000000000232C550>}
1
2
3
4
8.?singledispatchmethod(func)
將一個方法轉(zhuǎn)換為單分派泛型方法
要定義一個泛型方法,應(yīng)使用@singledispatchmethod裝飾器進行裝飾
分派是作用于第一個非self或非cls參數(shù)的類型
如果要用“單分派泛型方法.register”,則@singledispatchmethod裝飾器必須是最外層的裝飾器
9.?update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,
updated=WRAPPER_UPDATES)
更新一個包裝器函數(shù)wrapper以使其類似于被包裝的函數(shù)wrapped
通過可選參數(shù)指明原函數(shù)的那些屬性要直接被賦給wrapper函數(shù)的匹配屬性的元組,并且這些wrapper函數(shù)的屬性將使用原函數(shù)的對應(yīng)屬性來更新
這些參數(shù)的默認值是模塊級常量WRAPPER_ASSIGNMENTS(文檔字符串)以及WRAPPER_UPDATES(實例字典__dict__)
出于內(nèi)省和其他目的訪問原始函數(shù),此函數(shù)會自動為wrapper添加一個指向被包裝函數(shù)的__wrapped__屬性
10.?@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS,
updated=WRAPPER_UPDATES)
便捷函數(shù),用于在定義包裝器函數(shù)時發(fā)起調(diào)用update_wrapper()作為函數(shù)裝飾器
等價于partial(update_wrapper, wrapped=wrapped, assigned=assigned,
updated=updated)
示例:
運行結(jié)果: ?# 其中一次運行結(jié)果
--------func1:
wrapper
None
{}
--------func2:
func2
func2
{'__wrapped__': <function func2 at 0x0000000002318DC0>}