Python 的 reduce():從函數式到 Pythonic 風格
目錄
探索 Python 中的函數式編程
Python 的 reduce() 入門
必需的參數:函數和可迭代的
可選參數:初始化器
使用 Python 的 reduce() 減少可迭代對象
對數值求和
乘以數值
尋找最小值和最大值
檢查所有值是否為真
檢查任何值是否為真
比較reduce() 和accumulate()
考慮性能和可讀性
性能是關鍵
可讀性計數
結論
Pythonreduce()是一個函數,它實現了一種稱為折疊或縮減的數學技術。reduce()當您需要將函數應用于可迭代對象并將其減少為單個累積值時非常有用。Pythonreduce()在具有函數式編程背景的開發人員中很受歡迎,但 Python 可以提供更多。
在本教程中,您將介紹它的reduce()工作原理以及如何有效地使用它。您還可以涵蓋一些替代Python的工具,可以將更多的Python化,可讀性和效率比reduce()。
在本教程中,您將學習:
Python 的reduce()工作原理
什么是較常見的還原用例是
如何使用解決這些用例reduce()
哪些替代 Python 工具可用于解決這些相同的用例
有了這些知識,您將能夠決定在解決 Python 中的歸約或折疊問題時使用哪些工具。
為了更好地理解Python的的reduce(),這將是有幫助的一些以前的知識如何與工作的Python iterables,尤其是如何循環使用它們一for環。
探索 Python 中的函數式編程
函數式編程是一種基于將問題分解為一組單獨函數的編程范式。理想情況下,每個函數只接受一組輸入參數并產生輸出。
在函數式編程中,函數沒有任何影響它們為給定輸入產生的輸出的內部狀態。這意味著無論何時您使用相同的一組輸入參數調用函數,您都會得到相同的結果或輸出。
在函數式程序中,輸入數據流經一組函數。每個函數對其輸入進行操作并產生一些輸出。函數式編程盡量避免可變數據類型和狀態更改。它適用于在函數之間流動的數據。
函數式編程的其他核心特性包括:
使用遞歸而不是循環或其他結構作為主要的流程控制結構
專注于列表或數組處理
將重點放在什么是來計算,而不是如何計算它
使用避免副作用的純函數
采用高階函數
這個列表中有幾個重要的概念。以下是其中一些的詳細信息:
遞歸是一種技術,其中函數直接或間接調用自身以進行循環。它允許程序循環遍歷長度未知或不可預測的數據結構。
純函數是完全沒有副作用的函數。換句話說,它們是不更新或修改程序中的任何全局變量、對象或數據結構的函數。這些函數產生的輸出僅取決于輸入,這更接近于數學函數的概念。
高階函數是通過將函數作為參數、返回函數或兩者兼有來對其他函數進行操作的函數,就像Python 裝飾器一樣。
由于 Python 是一種多范式編程語言,它提供了一些支持函數式編程風格的工具:
函數作為一等對象
遞歸能力
匿名函數?lambda
迭代器和生成器
標準模塊,如functools和itertools
工具喜歡map(),filter(),reduce(),sum(),len(),any(),all(),min(),max(),等
盡管 Python并未受到函數式編程語言的嚴重影響,但早在 1993 年,對上面列出的一些函數式編程功能的需求就很明確。
作為回應,該語言中添加了幾個功能性工具。根據Guido van Rossum 的說法,它們是由社區成員貢獻的:
Python的收購lambda,reduce(),filter()和map()中,(我相信)Lisp的黑客誰錯過了他們的禮貌和提交工作補丁。(來源)
多年來,新功能,如列表內涵,發電機表情,和內置的功能,如sum(),min(),max(),all(),和any()被視為Python的替代品map(),filter()和reduce()。Guido計劃?從 Python 3 中的語言中刪除map(),?filter(),?reduce(), 甚至lambda。
幸運的是,這次移除并沒有生效,主要是因為 Python 社區不想放棄這些流行的功能。它們仍然存在并且仍然在具有強大函數式編程背景的開發人員中廣泛使用。
在本教程中,您將介紹如何在reduce()不使用for循環的情況下使用 Python處理可迭代對象并將它們減少為單個累積值。您還將了解一些 Python 工具,您可以使用這些工具reduce()來使您的代碼更具 Python 風格、可讀性和效率。
Python 入門?reduce()
Pythonreduce()實現了一種通常稱為折疊或縮減的數學技術。當您將項目列表減少到單個累積值時,您正在執行折疊或減少。Pythonreduce()對任何可迭代對象(不僅僅是列表)進行操作,并執行以下步驟:
將函數(或可調用)應用于可迭代對象中的前兩項并生成部分結果。
使用該部分結果以及可迭代對象中的第三項,生成另一個部分結果。
重復這個過程直到迭代用完,然后返回一個累積值。
Python 背后的想法reduce()是采用現有函數,將其累積應用于迭代中的所有項目,并生成單個最終值。通常,Pythonreduce()可以方便地處理迭代,而無需編寫顯式for循環。由于reduce()是用 C 編寫的,它的內部循環可以比顯式 Pythonfor循環更快。
Python的reduce()原本是內置函數(現在仍然是Python的2.X),但它被轉移到functools.reduce()在Python的3.0。這個決定是基于一些可能的性能和可讀性問題。
另一個原因,用于移動reduce()到functools被引入的內置函數喜歡sum(),any(),all(),max(),min(),和len(),其中提供處理共同使用情況的更有效的,可讀的,和Python化方式reduce()。reduce()在本教程的后面部分,您將學習如何使用它們。
在 Python 3.x 中,如果您需要使用reduce(),那么您首先必須通過以下方式之一使用語句將函數導入當前范圍:import
import functools然后使用完全限定的名稱,如functools.reduce().
from functools import reduce然后reduce()直接調用。
根據該文件的reduce(),該功能具有以下特征:
functools.reduce(function, iterable[, initializer])
Python 文檔還指出,reduce()它大致相當于以下 Python 函數:
def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value
像這個 Python 函數一樣,它reduce()通過iterable從左到右將兩個參數的函數應用于循環中的項目,最終減少iterable到單個累積value.
Pythonreduce()還接受第三個可選參數initializer,該參數為計算或減少提供種子值。
在接下來的兩節中,您將深入了解 Python 的reduce()工作原理及其每個參數背后的含義。
所需的參數:function和iterable
Python 的第一個參數reduce()是一個雙參數函數,方便地稱為function.?此函數將應用于迭代中的項目以累積計算最終值。
盡管官方文檔將 的第一個參數reduce()稱為“具有兩個參數的函數”,但您可以傳遞任何 Python 可調用對象,reduce()只要該可調用對象接受兩個參數即可。可調用對象包括類、實現稱為 的特殊方法的__call__()實例、實例方法、類方法、靜態方法和函數。
注意:有關 Python 可調用對象的更多詳細信息,您可以查看 Python文檔并向下滾動到“可調用類型”。
iterable顧名思義,第二個必需參數將接受任何 Python 可迭代對象。這包括列表、元組、range對象、生成器、迭代器、集合、字典鍵和值,以及您可以迭代的任何其他 Python 對象。
注意:如果您將迭代器傳遞給 Python 的reduce(),則該函數將需要耗盡迭代器才能獲得最終值。因此,手頭的迭代器不會保持惰性。
要了解如何reduce()工作,你會寫,計算兩者之和功能的數字和打印相當于數學運算到屏幕上。這是代碼:
>>>
>>> def my_add(a, b): ... result = a + b ... print(f"{a} + {b} = {result}") ... return result
此函數計算aand的總和,b使用f-string打印帶有操作的消息,并返回計算結果。這是它的工作原理:
>>>
>>> my_add(5, 5) 5 + 5 = 10 10
my_add()是一個雙參數函數,因此您可以將它reduce()與可迭代對象一起傳遞給 Python?,以計算可迭代對象中各項的累積總和。查看以下使用數字列表的代碼:
>>>
>>> from functools import reduce >>> numbers = [0, 1, 2, 3, 4] >>> reduce(my_add, numbers) 0 + 1 = 1 1 + 2 = 3 3 + 3 = 6 6 + 4 = 10 10
當您調用reduce()、傳遞my_add()和numbers作為參數時,您會得到一個輸出,其中顯示了reduce()為得出最終結果而執行的所有操作10。在這種情況下,操作等效于((((0 + 1) + 2) + 3) + 4) = 10。
上例中的 調用reduce()適用my_add()于numbers(0和1) 中的前兩項,并1作為結果獲取。然后reduce()調用my_add()using1和numbers(即2)中的下一項作為參數,得到3結果。重復該過程,直到numbers用完項目并reduce()返回 的最終結果10。
可選參數:?initializer
Python 的第三個參數reduce(),稱為initializer,是可選的。如果您向 提供值initializer,reduce()則將其function作為第一個參數提供給第一次調用。
這意味著第一次調用function將使用的值initializer和第一項iterable來執行它的第一部分計算。在此之后,reduce()繼續處理 的后續項目iterable。
下面是你用一個例子my_add()與initializer設置為100:
>>>
>>> from functools import reduce >>> numbers = [0, 1, 2, 3, 4] >>> reduce(my_add, numbers, 100) 100 + 0 = 100 100 + 1 = 101 101 + 2 = 103 103 + 3 = 106 106 + 4 = 110 110
由于您提供了100to的值initializer,Pythonreduce()在第一次調用中使用該值作為 的第一個參數my_add()。請注意,在第一次迭代中,my_add()使用100和0作為 的第一項numbers來執行計算100 + 0 = 100。
另一點要注意的是,如果您為 提供值initializer,則將reduce()比沒有 時多執行一次迭代initializer。
如果您打算使用reduce()處理可能為空的可迭代對象,那么為 提供值是一種很好的做法initializer。reduce()當iterable為空時,Python將使用此值作為其默認返回值。如果您不提供initializer值,reduce()則將引發TypeError.?看看下面的例子:
>>>
>>> from functools import reduce >>> # Using an initializer value >>> reduce(my_add, [], 0) # Use 0 as return value 0 >>> # Using no initializer value >>> reduce(my_add, []) # Raise a TypeError with an empty iterable Traceback (most recent call last): File "
如果您使用空調reduce()用iterable,則該函數將返回提供給 的值initializer。如果您不提供 an?initializer,則在處理空可迭代對象時reduce()將引發 a?TypeError。
注意:要深入了解 Python 回溯是什么,請查看了解 Python 回溯。
現在您已經熟悉了reduce()工作原理,您已經準備好學習如何將其應用于一些常見的編程問題。
使用 Python 減少可迭代對象?reduce()
到目前為止,您已經了解了 Python 的reduce()工作原理以及如何使用它來減少使用用戶定義函數的可迭代對象。您還了解了每個參數的含義reduce()以及它們的工作原理。
在本節中,您將了解一些常見用例reduce()以及如何使用該函數解決它們。您還將了解一些可替代的 Python 工具,您可以使用它們reduce()來使您的代碼更加 Python 化、高效和可讀。
對數值求和
在"Hello, World!"Python的的reduce()是和使用情況。它涉及計算數字列表的累積總和。假設您有一個數字列表,例如[1, 2, 3, 4].?它的總和將為1 + 2 + 3 + 4 = 10。以下是如何使用 Pythonfor循環解決此問題的快速示例:
>>>
>>> numbers = [1, 2, 3, 4] >>> total = 0 >>> for num in numbers: ... total += num ... >>> total 10
該for循環遍歷每個值numbers并累加它們total。最終結果是所有值的總和,在本例中為10。在這個例子中使用的變量total有時稱為累加器。
這可以說是 Python 的reduce().?要使用 實現此操作reduce(),您有多種選擇。其中一些包括reduce()與以下功能之一一起使用:
甲用戶定義函數
一個lambda函數
一個函數調用?operator.add()
要使用用戶定義的函數,您需要編寫一個將兩個數字相加的函數。然后您可以將該功能與reduce().?對于此示例,您可以my_add()按如下方式重寫:
>>>
>>> def my_add(a, b): ... return a + b ... >>> my_add(1, 2) 3
my_add()將兩個數字a和相加b,并返回結果。使用my_add()到位,您可以使用reduce()來計算 Python 可迭代對象中的值的總和。就是這樣:
>>>
>>> from functools import reduce >>> numbers = [1, 2, 3, 4] >>> reduce(my_add, numbers) 10
調用reduce()應用于my_add()中的項目numbers以計算它們的累積總和。最終的結果是10,正如預期的那樣。
您還可以使用lambda函數執行相同的計算。在這種情況下,您需要一個lambda將兩個數字作為參數并返回它們總和的函數。看看下面的例子:
>>>
>>> from functools import reduce >>> numbers = [1, 2, 3, 4] >>> reduce(lambda a, b: a + b, numbers) 10
該lambda函數接受兩個參數并返回它們的總和。在循環中reduce()應用該lambda函數來計算 中項目的累積總和numbers。
同樣,您可以利用名為operator.?該模塊導出了一系列與 Python 的內在運算符相對應的函數。對于手頭的問題,您可以operator.add()與 Python 的reduce().?查看以下示例:
>>>
>>> from operator import add >>> from functools import reduce >>> add(1, 2) 3 >>> numbers = [1, 2, 3, 4] >>> reduce(add, numbers) 10
在這個例子中,add()接受兩個參數并返回它們的總和。因此,您可以使用add()withreduce()來計算 的所有項目的總和numbers。由于add()是用 C 編寫的并針對效率進行了優化,因此在reduce()用于解決 sum 用例時,它可能是您的最佳選擇。請注意,使用 ofoperator.add()也比使用lambda函數更具可讀性。
sum 用例在編程中非常普遍,以至于 Python 從2.3 版開始就包含了一個專用的內置函數sum()來解決它。sum()被聲明為sum(iterable[, start]).
start是一個可選參數sum(),默認為0。該函數將 的值與從左到右start的項目相加iterable并返回總數。看看下面的例子:
>>>
>>> numbers = [1, 2, 3, 4] >>> sum(numbers) 10
由于sum()是內置函數,因此您無需導入任何內容。它隨時可供您使用。Usingsum()是解決 sum 用例的最 Pythonic 的方法。它干凈、易讀且簡潔。它遵循 Python 的核心原則:
簡單勝于復雜。(來源)
sum()與使用reduce()或for循環相比,添加到語言中在可讀性和性能方面是一個巨大的勝利。
注意:有關將 的性能reduce()與其他 Python 縮減工具的性能進行比較的更多詳細信息,請查看性能是關鍵部分。
如果您正在處理 sum 用例,那么好的做法建議使用sum().
乘以數值
該產品使用情況Python的的reduce()是神似和使用情況,但此時的操作是乘法。換句話說,您需要計算可迭代對象中所有值的乘積。
例如,假設您有 list?[1, 2, 3, 4]。其產品將1 * 2 * 3 * 4 = 24。您可以使用 Pythonfor循環來計算它。查看以下示例:
>>>
>>> numbers = [1, 2, 3, 4] >>> product = 1 >>> for num in numbers: ... product *= num ... >>> product 24
循環遍歷 中的項目numbers,將每個項目乘以上一次迭代的結果。在這種情況下,累加器的起始值product應該是1而不是0。由于任何乘以零的數字為零,因此 的起始值0將始終使您的乘積等于0。
這種計算也是 Python 的reduce().?同樣,您將介紹解決問題的三種方法。您將使用reduce():
用戶定義的函數
一個lambda函數
一個函數調用?operator.mul()
對于選項 1,您需要編寫一個自定義函數,該函數接受兩個參數并返回它們的乘積。然后,您將使用此函數 withreduce()來計算可迭代對象中項目的乘積。看看下面的代碼:
>>>
>>> from functools import reduce >>> def my_prod(a, b): ... return a * b ... >>> my_prod(1, 2) 2 >>> numbers = [1, 2, 3, 4] >>> reduce(my_prod, numbers) 24
該函數my_prod()將兩個數字相乘,a并且b。調用reduce()迭代 的項目numbers并通過應用于my_prod()連續項目來計算它們的乘積。最終結果是 中所有項的乘積numbers,在本例中為24。
如果您更喜歡使用lambda函數來解決這個用例,那么您需要一個接受兩個參數并返回它們的乘積的函數。下面是一個例子:
>>>
>>> from functools import reduce >>> numbers = [1, 2, 3, 4] >>> reduce(lambda a, b: a * b, numbers) 24
匿名函數通過在reduce()迭代 時將連續項相乘來實現魔術numbers。同樣,結果是 中所有項目的乘積numbers。
您還可以operator.mul()用于處理產品用例。operator.mul()接受兩個數字并返回它們相乘的結果。這是解決手頭問題的正確功能。查看以下示例:
>>>
>>> from operator import mul >>> from functools import reduce >>> mul(2, 2) 4 >>> numbers = [1, 2, 3, 4] >>> reduce(mul, numbers) 24
由于mul()高度優化,如果您使用此函數而不是用戶定義的函數或lambda函數,您的代碼將執行得更好。請注意,此解決方案也更具可讀性。
最后,如果您使用的是Python 3.8,那么您可以訪問此用例的更 Pythonic 和可讀的解決方案。Python 3.8 添加了一個名為 的新函數prod(),它位于Pythonmath模塊中。此函數類似于sum()但返回一個start值乘以一個iterable數字的乘積。
在 的情況下math.prod(),參數start是可選的,默認為1。這是它的工作原理:
>>>
>>> from math import prod >>> numbers = [1, 2, 3, 4] >>> prod(numbers) 24
與使用reduce().?因此,如果您使用的是Python 3.8并且產品歸約是您代碼中的常見操作,那么使用math.prod()而不是 Python 的reduce().
尋找最小值和最大值
在可迭代對象中找到最小值和最大值的問題也是一個歸約問題,您可以使用 Python 的reduce().?這個想法是比較迭代中的項目以找到最小值或最大值。
假設您有數字列表[3, 5, 2, 4, 7, 1]。在此列表中,最小值為1,最大值為7。要查找這些值,您可以使用 Pythonfor循環。查看以下代碼:
>>>
>>> numbers = [3, 5, 2, 4, 7, 1] >>> # Minimum >>> min_value, *rest = numbers >>> for num in rest: ... if num < min_value: ... min_value = num ... >>> min_value 1 >>> # Maximum >>> max_value, *rest = numbers >>> for num in rest: ... if num > max_value: ... max_value = num ... >>> max_value 7
兩個循環都迭代 in 中的項目rest并根據連續比較的結果更新min_value或的值max_value。請注意,最初min_value并max_value保持數字3,它是 中的第一個值numbers。該變量rest將剩余的值保存在numbers.?換句話說,rest = [5, 2, 4, 7, 1]。
注:在上面的例子中,你使用Python迭代拆包經營者(*)來解壓或擴展中的值numbers分為兩個變量。在第一種情況下,凈效果是min_value獲取 中的第一個值numbers,即3,并rest收集列表中的其余值。
查看以下示例中的詳細信息:
>>>
>>> numbers = [3, 5, 2, 4, 7, 1] >>> min_value, *rest = numbers >>> min_value 3 >>> rest [5, 2, 4, 7, 1] >>> max_value, *rest = numbers >>> max_value 3 >>> rest [5, 2, 4, 7, 1]
*當您需要將一個序列或可迭代對象解包為多個變量時,Python 可迭代解包運算符 (?) 非常有用。
為了更好地理解 Python 中的解包操作,您可以查看PEP 3132 Extended Iterable Unpacking和PEP 448 Additional Unpacking Generalizations。
現在,考慮如何使用 Python 的reduce().?同樣,您可以lambda根據需要使用用戶定義的函數或函數。
以下代碼實現了一個使用兩個不同用戶定義函數的解決方案。第一個函數將接受兩個參數a和b,并返回它們的最小值。第二個函數將使用類似的過程,但它會返回最大值。
以下是函數以及如何將它們與 Pythonreduce()一起使用以查找可迭代對象中的最小值和最大值:
>>>
>>> from functools import reduce >>> # Minimum >>> def my_min_func(a, b): ... return a if a < b else b ... >>> # Maximum >>> def my_max_func(a, b): ... return a if a > b else b ... >>> numbers = [3, 5, 2, 4, 7, 1] >>> reduce(my_min_func, numbers) 1 >>> reduce(my_max_func, numbers) 7
當您reduce()使用my_min_func()和運行時my_max_func(),您將numbers分別獲得 中的最小值和最大值。reduce()迭代 的項numbers,按累積對比較它們,最后返回最小值或最大值。
注意:為了實現my_min_func()and?my_max_func(),您使用了 Python 條件表達式或三元運算符作為return值。要更深入地了解什么是條件表達式及其工作原理,請查看Python 中的條件語句 (if/elif/else)。
您還可以使用lambda函數來解決最小值和最大值問題。看看下面的例子:
>>>
>>> from functools import reduce >>> numbers = [3, 5, 2, 4, 7, 1] >>> # Minimum >>> reduce(lambda a, b: a if a < b else b, numbers) 1 >>> # Maximum >>> reduce(lambda a, b: a if a > b else b, numbers) 7
這一次,您使用兩個lambda函數來確定 ifa小于或大于b。在這種情況下,Pythonreduce()將lambda函數應用于 中的每個值numbers,并將其與先前計算的結果進行比較。在過程結束時,您將獲得最小值或最大值。
最小和最大問題在編程中非常普遍,以至于 Python 添加了內置函數來執行這些縮減。這些函數被方便地稱為min()and?max(),您無需導入任何內容即可使用它們。以下是它們的工作原理:
>>>
>>> numbers = [3, 5, 2, 4, 7, 1] >>> min(numbers) 1 >>> max(numbers) 7
當您使用min()andmax()查找可迭代對象中的最小和最大項時,與使用 Python 的reduce().?此外,由于min()和max()是高度優化的 C 函數,您也可以說您的代碼將更加高效。
所以,在 Python 中解決這個問題時,最好使用min()andmax()而不是reduce().
檢查所有值是否為真
該全真用例Python的中reduce()涉及找出在是否所有的項目迭代是真實的。要解決這個問題,您可以reduce()與用戶定義的函數或lambda函數一起使用。
您將首先編寫一個for循環來確定迭代中的所有項目是否都為真。這是代碼:
>>>
>>> def check_all_true(iterable): ... for item in iterable: ... if not item: ... return False ... return True ... >>> check_all_true([1, 1, 1, 1, 1]) True >>> check_all_true([1, 1, 1, 1, 0]) False >>> check_all_true([]) True
如果 中的所有值iterable都為真,則check_all_true()返回True。否則,它返回False。它還返回True空的可迭代對象。check_all_true()實施短路評估。這意味著該函數會在不處理 中的其余項的情況下,在發現錯誤值時立即返回iterable。
要使用 Python 的 解決這個問題reduce(),您需要編寫一個函數,該函數接受兩個參數并True在兩個參數都為真時返回。如果一個或兩個參數為假,則函數將返回False。這是代碼:
>>>
>>> def both_true(a, b): ... return bool(a and b) ... >>> both_true(1, 1) True >>> both_true(1, 0) False >>> both_true(0, 0) False
這個函數有兩個參數,a和b。然后您使用and運算符來測試兩個參數是否都為真。True如果兩個參數都為真,則返回值。否則,它將是False。
在 Python 中,以下對象被認為是 false:
像None和這樣的常數False
數值類型與零值一樣0,0.0,0j,Decimal(0),和Fraction(0, 1)
空序列和喜歡"",(),[],{},set(),和range(0)
實現物體__bool__()與返回值False或__len__()與返回值0
任何其他對象都將被視為真實。
您需要使用bool()將 的返回值and轉換為True或False。如果您不使用bool(),那么您的函數將不會按預期運行,因為and返回表達式中的對象之一而不是Trueor?False。查看以下示例:
>>>
>>> a = 0 >>> b = 1 >>> a and b 0 >>> a = 1 >>> b = 2 >>> a and b 2
and如果為假,則返回表達式中的第一個值。否則,無論其真值如何,它都會返回表達式中的最后一個值。這就是為什么你需要bool()在這種情況下使用。bool()返回由計算布爾表達式或對象產生的布爾值(True或False)。使用bool()以下方法查看示例:
>>>
>>> a = 0 >>> b = 1 >>> bool(a and b) False >>> a = 1 >>> b = 2 >>> bool(a and b) True
bool()將始終返回True或False在評估手頭的表達式或對象之后返回。
注:為了更好地理解Python的運算符和表達式,你可以看看在Python運算符和表達式。
您可以通過both_true()toreduce()檢查迭代的所有項目是否為真。這是它的工作原理:
>>>
>>> from functools import reduce >>> reduce(both_true, [1, 1, 1, 1, 1]) True >>> reduce(both_true, [1, 1, 1, 1, 0]) False >>> reduce(both_true, [], True) True
如果您將both_true()參數作為參數傳遞給reduce(),那么您將得到True可迭代對象中的所有項目是否為真。否則,你會得到False.
在第三個示例中,您傳遞True給initializerofreduce()以獲得與 a 相同的行為check_all_true()并避免 a?TypeError。
您還可以使用lambda函數來解決reduce().?這里有些例子:
>>>
>>> from functools import reduce >>> reduce(lambda a, b: bool(a and b), [0, 0, 1, 0, 0]) False >>> reduce(lambda a, b: bool(a and b), [1, 1, 1, 2, 1]) True >>> reduce(lambda a, b: bool(a and b), [], True) True
此lambda函數與both_true()返回值非常相似并使用相同的表達式。True如果兩個參數都為真,則返回。否則,它返回False。
請注意,與 不同check_all_true(),當您用于reduce()解決全真用例時,沒有短路評估,因為reduce()在遍歷整個可迭代對象之前不會返回。這會給您的代碼增加額外的處理時間。
例如,假設您有一個列表lst = [1, 0, 2, 0, 0, 1],您需要檢查其中的所有項目lst是否為真。在這種情況下,check_all_true()將在其循環處理第一對項目(1和0)后立即結束,因為它0為假。您不需要繼續迭代,因為您手頭的問題已經有了答案。
另一方面,reduce()解決方案在處理完lst.?那是五次迭代之后。現在想象一下,如果您正在處理一個大型可迭代對象,這會對您的代碼性能產生什么影響!
幸運的是,Python 提供了正確的工具,可以以 Python 風格、可讀且高效的方式解決全真問題:內置函數all()。
您可以使用all(iterable)來檢查中的所有項目iterable是否為真。以下是all()工作原理:
>>>
>>> all([1, 1, 1, 1, 1]) True >>> all([1, 1, 1, 0, 1]) False >>> all([]) True
all()循環遍歷迭代中的項目,檢查每個項目的真值。如果all()發現錯誤的項目,則返回False。否則,它返回True。如果您all()使用空的可迭代對象調用,那么您會得到,True因為空的可迭代對象中沒有錯誤的項目。
all()是針對性能進行了優化的 C 函數。此功能也使用短路評估來實現。因此,如果您正在處理 Python 中的全真問題,那么您應該考慮使用all()而不是reduce().
檢查任何值是否為真
Python 的另一個常見用例reduce()是any-true 用例。這一次,您需要確定迭代中是否至少有一項為真。要解決這個問題,您需要編寫一個函數,該函數接受一個可迭代對象,并True在可迭代對象中的任何項為真時返回,False否則返回。看看這個函數的以下實現:
>>>
>>> def check_any_true(iterable): ... for item in iterable: ... if item: ... return True ... return False ... >>> check_any_true([0, 0, 0, 1, 0]) True >>> check_any_true([0, 0, 0, 0, 0]) False >>> check_any_true([]) False
如果至少有一項iterable為真,則check_any_true()返回True。False僅當所有項目都為假或可迭代對象為空時才返回。此函數還實現了短路評估,因為它會在找到真值(如果有)后立即返回。
要使用 Python 的 解決此問題reduce(),您需要編寫一個函數,該函數接受兩個參數并True在其中至少一個為真時返回。如果兩者都為假,則該函數應返回False。
這是此功能的可能實現:
>>>
>>> def any_true(a, b): ... return bool(a or b) ... >>> any_true(1, 0) True >>> any_true(0, 1) True >>> any_true(0, 0) False
any_true()返回True如果其參數的至少一個是真的。如果兩個參數都為假,則any_true()返回False。與both_true()上一節一樣,any_true()用于bool()將表達式的結果轉換a or b為True或False。
在Python的or運營商從工作方式略有不同and。它返回表達式中的第一個真實對象或最后一個對象。查看以下示例:
>>>
>>> a = 1 >>> b = 2 >>> a or b 1 >>> a = 0 >>> b = 1 >>> a or b 1 >>> a = 0 >>> b = [] >>> a or b []
Pythonor運算符返回第一個 true 對象,或者如果兩者都為 false,則返回最后一個對象。因此,您還需要使用bool()從any_true().
一旦你有了這個功能,你就可以繼續減少。看看以下對 的調用reduce():
>>>
>>> from functools import reduce >>> reduce(any_true, [0, 0, 0, 1, 0]) True >>> reduce(any_true, [0, 0, 0, 0, 0]) False >>> reduce(any_true, [], False) False
您已經使用 Python 的reduce().?請注意,在第三個示例中,您傳遞False給 的初始值設定項reduce()以重現原始行為check_any_true()并避免TypeError.
注意:與上一節中的示例一樣,這些示例reduce()不進行短路評估。這意味著它們會影響代碼的性能。
您還可以使用lambdawith 函數reduce()來解決 any-true 用例。您可以這樣做:
>>>
>>> from functools import reduce >>> reduce(lambda a, b: bool(a or b), [0, 0, 1, 1, 0]) True >>> reduce(lambda a, b: bool(a or b), [0, 0, 0, 0, 0]) False >>> reduce(lambda a, b: bool(a or b), [], False) False
此lambda功能與any_true().?True如果它的兩個參數中的任何一個為真,它就會返回。如果兩個參數都為假,則返回False。
即使此解決方案只需要一行代碼,它仍然會使您的代碼不可讀或至少難以理解。同樣,Python 提供了一個工具,可以在不使用reduce()的情況下有效地解決任何真問題:內置函數any()。
any(iterable)遍歷 中的項目iterable,測試每個項目的真值,直到找到一個真項目。該函數True在找到真值后立即返回。如果any()沒有找到真值,則返回False。下面是一個例子:
>>>
>>> any([0, 0, 0, 0, 0]) False >>> any([0, 0, 0, 1, 0]) True >>> any([]) False
同樣,您無需導入any()即可在代碼中使用它。any()按預期工作。False如果迭代中的所有項目都為假,則返回。否則,它返回True。請注意,如果您any()使用空的可迭代對象調用,那么您會得到,False因為空的可迭代對象中沒有真正的項目。
與 一樣all(),any()是針對性能進行了優化的 C 函數。它還使用短路評估來實現。因此,如果您正在處理 Python 中的 any-true 問題,請考慮使用any()代替reduce().
比較reduce()和accumulate()
一個Python函數調用accumulate()中的生活itertools和行為方式類似reduce()。accumulate(iterable[, func])接受一個必需的參數 ,iterable它可以是任何 Python 可迭代的。可選的第二個參數func需要是一個函數(或一個可調用對象),它接受兩個參數并返回一個值。
accumulate()返回一個迭代器。此迭代器中的每一項都將是func執行計算的累積結果。默認計算是總和。如果您不向 提供函數accumulate(),則結果迭代器中的每一項都將是前iterable一項加上手頭項的累加和。
查看以下示例:
>>>
>>> from itertools import accumulate >>> from operator import add >>> from functools import reduce >>> numbers = [1, 2, 3, 4] >>> list(accumulate(numbers)) [1, 3, 6, 10] >>> reduce(add, numbers) 10
請注意,結果迭代器中的最后一個值與reduce()返回的值相同。這是這兩個函數之間的主要相似之處。
注意:由于accumulate()返回一個迭代器,所以需要調用list()來消費迭代器并得到一個列表對象作為輸出。
如果,另一方面,您提供的兩個參數的函數(或可調用)到func的參數accumulate()中所產生的,那么項目的迭代器將累加結果的計算由執行func。這是一個使用的示例operator.mul():
>>>
>>> from itertools import accumulate >>> from operator import mul >>> from functools import reduce >>> numbers = [1, 2, 3, 4] >>> list(accumulate(numbers, mul)) [1, 2, 6, 24] >>> reduce(mul, numbers) 24
在此示例中,您可以再次看到 的返回值中的最后一項accumulate()等于 返回的值reduce()。
考慮性能和可讀性
Python 的reduce()性能可能非常糟糕,因為它通過多次調用函數來工作。這會使您的代碼變慢且效率低下。使用reduce()時,用復雜的用戶定義的函數或使用但也容易影響你的代碼的可讀性lambda功能。
在本教程中,您已經了解到 Python 提供了一堆可以優雅地替換 的工具,reduce()至少對于它的主要用例是這樣。到目前為止,以下是您閱讀的主要內容:
reduce()盡可能使用專用函數來解決 Python 的用例。sum(),?all(),?any(),?max(),?min(),?len(),?math.prod(), 等函數將使您的代碼更快、更易讀、更易于維護和Pythonic。
避免復雜的用戶定義的函數使用時reduce()。這些類型的函數會使您的代碼難以閱讀和理解。您可以改用顯式且可讀的for循環。
lambda使用時避免復雜的函數reduce()。它們還可能使您的代碼不可讀和令人困惑。
第二點和第三點是圭多自己在說以下內容時的擔憂:
所以現在reduce()。這實際上是我一直最討厭的一個,因為除了幾個涉及+or 的例子*,幾乎每次我看到一個reduce()帶有非平凡函數參數的調用時,我都需要拿起筆和紙來繪制實際被喂食的內容在我理解應該做什么之前進入該功能reduce()。所以在我看來, 的適用性reduce()幾乎僅限于關聯運算符,在所有其他情況下,最好明確寫出累加循環。(來源)
接下來的兩節將幫助您在代碼中實現這一一般建議。他們還提供了一些額外的建議,可以幫助您reduce()在真正需要使用 Python 時有效地使用它。
性能是關鍵
如果您打算使用reduce()來解決本教程中介紹的用例,那么與使用專用內置函數的代碼相比,您的代碼會慢得多。在以下示例中,您將使用timeit.timeit()來快速測量一小段 Python 代碼的執行時間并了解它們的總體性能。
timeit()?需要幾個參數,但對于這些示例,您只需要使用以下參數:
stmt?持有聲明,你需要時間。
setup為一般設置采用附加語句,例如importstatements。
globals保存一個包含全局命名空間的字典,你需要使用它來運行stmt.
看看下面的例子,時間總和用例使用reduce()不同的工具并使用 Pythonsum()進行比較:
>>>
>>> from functools import reduce >>> from timeit import timeit >>> # Using a user-defined function >>> def add(a, b): ... return a + b ... >>> use_add = "functools.reduce(add, range(100))" >>> timeit(use_add, "import functools", globals={"add": add}) 13.443158069014316 >>> # Using a lambda expression >>> use_lambda = "functools.reduce(lambda x, y: x + y, range(100))" >>> timeit(use_lambda, "import functools") 11.998800784000196 >>> # Using operator.add() >>> use_operator_add = "functools.reduce(operator.add, range(100))" >>> timeit(use_operator_add, "import functools, operator") 5.183870767941698 >>> # Using sum() >>> timeit("sum(range(100))", globals={"sum": sum}) 1.1643308430211619
即使根據您的硬件您會得到不同的數字,您也可能會使用sum().?這個內置函數也是求和問題最具可讀性和 Pythonic 的解決方案。
注意:有關如何為代碼計時的更多詳細方法,請查看Python 計時器函數:監視代碼的三種方法。
您的第二個最佳選擇是reduce()與operator.add().?中的函數operator是用 C 編寫的,并針對性能進行了高度優化。因此,它們的性能應該比用戶定義的函數、lambda函數或for循環更好。
可讀性計數
在使用 Python 的reduce().?盡管reduce()通常會比 Pythonfor循環執行得更好,正如 Guido 自己所說的那樣,但干凈和Pythonic 的循環通常比使用reduce().
The?What's New In Python 3.0 guide強調了這個想法,它說以下內容:
functools.reduce()如果您確實需要,請使用它;然而,在 99% 的情況下,顯式for循環更具可讀性。(來源)
為了更好地理解可讀性的重要性,假設您開始學習 Python,并且您正在嘗試解決一個關于計算可迭代對象中所有偶數之和的練習。
如果您已經了解 Pythonreduce()并且過去做過一些函數式編程,那么您可能會想出以下解決方案:
>>>
>>> from functools import reduce >>> def sum_even(it): ... return reduce(lambda x, y: x + y if not y % 2 else x, it, 0) ... >>> sum_even([1, 2, 3, 4]) 6
在此函數中,您用于reduce()對可迭代對象中的偶數進行累積求和。該lambda函數接受兩個參數x和y,如果它們是偶數,則返回它們的總和。否則,它返回x,它保存前一個總和的結果。
此外,您設置initializer為 ,0因為否則您的總和將具有1( 中的第一個值iterable)的初始值,該值不是偶數并且會在您的函數中引入錯誤。
該函數按您的預期工作,您對結果感到滿意。但是,你繼續挖成Python,了解sum()和生成器表達式。您決定使用這些新工具重新設計您的函數,您的函數現在如下所示:
>>>
>>> def sum_even(iterable): ... return sum(num for num in iterable if not num % 2) ... >>> sum_even([1, 2, 3, 4]) 6
當您看到這段代碼時,您感到非常自豪,而且您應該這樣做。你做得很好!這是一個漂亮的 Python 函數,幾乎讀起來像簡單的英語。它也是高效和 Pythonic 的。你怎么認為?
結論
Pythonreduce()允許您使用 Python 可調用對象和lambda函數對可迭代對象執行歸約操作。reduce()將函數應用于迭代中的項目并將它們減少到單個累積值。
在本教程中,您學習了:
什么是縮減或折疊,以及何時可能有用
如何使用 Pythonreduce()解決常見的歸約問題,例如數字求和或乘法
您可以使用哪些Pythonic 工具來有效地替換reduce()您的代碼
有了這些知識,在解決 Python 中的歸約問題時,您將能夠決定哪些工具最適合您的編碼需求。
多年來,reduce()已被更多 Pythonic 工具所取代,例如sum()、min()、max()?all()、any()等。然而,reduce()仍然存在并且仍然在函數式程序員中流行。如果您對使用reduce()Python 或其任何替代方案有任何疑問或想法,請務必在下面的評論中發布它們。
Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。