這是我參與8月更文挑戰的第10天,活動詳情查看:8月更文挑戰html
Python標準庫包括許多強大的模塊,可讓你的代碼既乾淨又高效,今天咱們介紹的是functools
模塊,這個模塊提供了許多有用的高階函數,咱們能夠利用這些高階函數來實現緩存功能、重載、建立裝飾器等,會然咱們的代碼更加高效,好了,讓咱們來看一下,functools
模塊都給咱們提供了什麼呢。python
functools
模塊中提供了一個簡單卻又強大的緩存函數(裝飾器)lru_cache()
,lru_cache()
能夠直接以語法糖@
的形式做用於咱們的函數至上,爲咱們的函數提供緩存的功能,遵循最近最少使用的原則,將函數執行結果緩存,方便下次函數執行前查詢,若是函數的參數比較固定,邏輯比較固定,那麼將很是必要,能夠有效的減小函數的執行時間。緩存
import functools
import time
@functools.lru_cache(maxsize=32)
def add(a, b):
print("sleep 2s")
time.sleep(2)
return a+b
print("第1次調用:", add(4, 5)) # 第一次調用,無緩存,執行原函數,結果加入緩存
print("第2次調用:", add(4, 5)) # 第二次調用,參數與第一次相同,匹配到緩存,直接返回緩存,不執行原函數
print("第3次調用:", add(5, 5)) # 第三次調用,參數與以前不一樣,無緩存,執行原函數,結果加入緩存
print(add.cache_info()) # 檢查函數的緩存信息,它顯示緩存命中和未命中的數量
複製代碼
執行結果爲:markdown
sleep 2s
第1次調用: 9
第2次調用: 9
sleep 2s
第3次調用: 10
CacheInfo(hits=1, misses=2, maxsize=32, currsize=2)
Process finished with exit code 0
複製代碼
在這個例子中,咱們使用@lru_cache
裝飾器處理add()
函數並緩存執行的結果(能夠緩存32個結果)。爲了查看緩存是否真的有效,咱們使用了cache_info()
方法檢查函數的緩存信息,它顯示緩存命中和未命中的數量,經過結果咱們能夠看出,在第一次調用的時候並沒有緩存,add()
函數執行結果存入緩存,第二次的時候命中緩存,第三次的時候沒有命中緩存。app
若是你想要更細粒度的緩存,那麼你也能夠包含可選的typed=true
參數,這使得不一樣類型的參數被分別緩存。 例如,add(4.0, 5.0)
和add(4, 5)
將被視爲不一樣的調用。函數
其實在Python3.8的版本以後,還有另外一個能夠用於緩存的裝飾器叫作cached_property
,這個函數用於緩存類屬性的結果,若是你的類的屬性是而又不可變的,可使用這個。oop
您可能已經知道,可使用__lt__()
、__gt__()
和__eq__()
在Python中實現比較操做符,如<
、>
或==
。可是實現__lt__()
、__gt__()
、__eq__()
、__le__()
、__ge__()
多是至關繁瑣的,恰好functools
模塊包含total_ordering
'裝飾器,它能夠幫助咱們作到這一點。
咱們只須要實現__eq__()
,和__lt__()
、__gt__()
、__le__()
、__ge__()
中的任一個方法,其餘的它會幫咱們自動提供:post
from functools import total_ordering
@total_ordering
class MyNumber:
def __init__(self, value):
self.value = value
def __gt__(self, other):
return self.value > other.value
def __eq__(self, other):
return self.value == other.value
print(MyNumber(5) > MyNumber(3))
# True
print(MyNumber(1) < MyNumber(5))
# True
print(MyNumber(5) >= MyNumber(5))
# True
print(MyNumber(5) <= MyNumber(2))
# False
複製代碼
很明顯,它能夠幫助咱們減小代碼和提升可讀性。ui
這裏的偏函數和數學意義上的偏函數不是一個概念,functools
模塊中的partial
函數的做用是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會比調用原來的函數更簡單。理解起來是否是有些困惑,讓咱們來看一些實際的例子。 在Python中,int()
函數能夠把指定數字字符串轉換爲整數,默認是作十進制的轉換:url
print(int(1234))
# 1234
複製代碼
但int()
函數還提供了一個base
參數,默認值爲10
。若是傳入base
參數,就能夠作N進制的轉換,即,默認數字字符串的進制是N進制,要轉化爲10進制,例如:
print(int("1234", base=8))
# 默認1234位8進制的數字,轉化爲10進制:668
print(int("1234", base=16))
# 默認1234位16進制的數字,轉化爲10進制:4660
複製代碼
假設要轉換大量的8進制字符串,每次都傳入int(x, base=8)
顯得過於麻煩,因而,咱們想到,能夠定義一個int8()
的函數,默認把base=8
傳進去:
def int8(x, base=8):
return int(x, base)
print(int8("1234"))
複製代碼
這樣,就很是方便了。
其實functools.partial
的功能就是可以建立一個偏函數,不須要咱們本身定義int8()
,能夠直接使用下面的代碼建立一個新的函數int8
:
int2 = functools.partial(int, base=8)
print(int2('1234'))
複製代碼
因此簡單來講,functools.partial
的做用就是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,使得咱們調用這個新函數會更簡單。
咱們都知道嚴格意義上來講,在Python中函數重載是不可能實現的,但實際上有一個簡單的方法來實現和函數重載類似的功能,那就是使用functools
模塊中singledispatch
函數裝飾器。下面來看個例子:
@singledispatch
def show(obj):
print(obj, type(obj), "obj")
@show.register(str)
def _(text):
print(text, type(text), "str")
@show.register(int)
def _(n):
print(n, type(n), "int")
show(1234)
show("abcd")
show({"abcd": 1234})
複製代碼
結果爲:
1234 <class 'int'> int
abcd <class 'str'> str
{'abcd': 1234} <class 'dict'> obj
Process finished with exit code 0
複製代碼
可見爲show()
函數傳遞不一樣的類型參數,就調用不一樣的函數,表現不一樣的行爲,實際上singledispatch
實現的是單泛函數,給函數加上.register
方法,該方法支持綁定一個變量類型和一個函數。而後它返回一個被重載了的函數。當輸入值的類型是經過.register
綁定的類型時,就調用同時綁定的函數。
這一部分在我以前的文章有提到,感性趣的能夠看一下:內置裝飾器@functools.wrap
functools
模塊提供了許多有用的功能和裝飾器,能夠幫助咱們構建更好的代碼,我這裏提到的只是部份內容。感興趣的小夥伴能夠查看官方文檔functools — Higher-order functions and operations on callable objects
最後,感謝女友在工做和生活中的包容、理解與支持 !