Python 中驚人的 Functools 特性

Python 中驚人的 Functools 特性

我最近在閱讀 Django 的原始碼,我遇到了 @wraps 裝飾器,這讓我找到了 functools 文件,在那裡我發現了一些很棒的 functools 特性。這一發現導致了這篇文章的創作。本文將教你如何使用一些很棒的 functools 方法讓你的編碼更簡單。

什麼是functools?

functools 是一個 Python 內建模組,包含可以與其他函式互動的高階函式。可以在Python官方網站找到完整的 functools 文件。

網站連結:

https://docs。python。org/3/library/functools。html

lru_快取

當以相同的引數呼叫一個函式時,functools 模組中的這個裝飾器將 n 次函式呼叫儲存在快取中,從而節省了大量時間。

為了演示,假設我們有一個非常大的函式,需要很長時間才能執行。在此示例中,函式

a_heavy_operation()

需要 3 秒才能執行。

import timestart = time。time()def a_heavy_operation(): time。sleep(3) return 11 + 22print(a_heavy_operation())print(a_heavy_operation())print(time。time() - start)# 輸出# 33 #33 #6。024240255355835

執行上述程式碼大約需要 6 秒。對於上述功能,我們將新增 lru 快取。

import timefrom functools import lru_cachestart = time。time()@lru_cache()def a_heavy_operation(): time。sleep(3) return 11 + 22print(a_heavy_operation())print(a_heavy_operation())print(time。time() - start)# Output# 33# 33# 3。0158064365386963

看看使用 lru 快取如何使我們的程式碼執行得更快。Python 儲存函式的快取並檢索快取值,減少了我們的執行時間。

Wraps

在 functools 中使用 Wraps 來保留函式的細節。當我們裝飾一個函式時,函式的資訊就沒有了。我們在裝飾器包裝函式上使用 @wraps 裝飾器來防止這種情況。

看看這段程式碼就明白我的意思了。

from functools import lru_cachedef my_decorator(func): def log(*args, **kwargs): print(“Running ”) return func(*args, *kwargs) return log@my_decoratordef add(a, b): “”“my beautiful doc”“” return a + b

使用 -i 模式執行上面的程式碼,

python -i file。py

>>> add(1,2)Running 3>>> add(3,4)Running 7>>> add。__name__log>>> add。__doc__>>>

我們可以看到我們的裝飾器在前面的示例中執行正常,因為它在每次執行時始終“執行”。但是,我們函式的資訊已經丟失,無法返回名稱或文件字串。

我們有@wraps 來幫助我們解決這個問題。對程式碼進行以下更改。

from functools import wrapsdef my_decorator(func): @wraps(func) def log(*args, **kwargs): print(“Running ”) return func(*args, *kwargs) return log@my_decoratordef add(a, b): “”“my beautiful doc”“” return a + b

現在再次使用執行程式碼

python -i file。py

>>> add(1,2) Running 3 >>> add。__name__‘add’>>> add。__doc__‘my beautiful doc’>>>

現在功能資訊現在儲存在我們的功能中。

singledispatch

要建立通用函式,可以使用 singledispatch。通用函式是那些對各種資料型別執行相同操作的函式。

假設我想建立一個函式,該函式從多種資料型別的可迭代物件中返回第一個值。

def return_first_element(data): if isinstance(data, list): print(data[0]) elif isinstance(data, str): print(data。split()[0]) elif isinstance(data, dict): print(list(data。values())[0] ) else: print(print(data))

現在執行

python -i file。py

以互動模式執行程式碼。

>>> return_first_element({“Age”:20, “Height”: 180})20>>> return_first_element(“Hello Mr Python”)Hello>>> return_first_element([12,432,563]) 12>>>

我們的功能是有效的,但它並不乾淨。不建議在 Python 中使用 if/elif/else 語句來建立泛型函式。那麼,解決方案是什麼?singledispatch,當然。

讓我們對程式碼做一些修改。

from functools import singledispatch@singledispatchdef return_first_el(data): return data@return_first_el。register(list)def _(data): return data[0]@return_first_el。register(dict)def _(data): return list(data。values())[0]@return_first_el。register(str)def _(data): return data。split()[0]

要檢查結果,請使用python -i file。py

在互動模式下再次執行程式碼。

>>> return_first_el({“Age”:20, “Height”: 180}) 20 >>> return_first_el(“Hello Mr Python”) ‘你好’ >>> return_first_el([124, 765, 897]) 124 >> > return_first_el({12,31,1}) {1, 12, 31}

看看

return_first_el

當沒有資料型別與“set”匹配時,我們的函式如何充當回退函式。

看看我們的程式碼現在乾淨多了;singledispatch 使得新增更多資料型別變得更容易,並且每個資料型別現在都有自己的位置,我們可以在其中對資料執行進一步的操作。

total_ordering

total_ordering 裝飾器在面向物件程式設計中節省了大量時間。

考慮這個例子,下面的類聲明瞭一個

Man

具有 name 和 age 屬性以及 (=) __

eq__

和 (<) __l

t__

dunder 方法的類。

class Man: def __init__(self, name, age): self。name = name self。age = age def __eq__(self, o): return self。age == o。age def __lt__(self, o): return self。age < o。age

讓我們看看如果我們執行程式碼會發生什麼。

>>> obj = Man(“Vivek”, 20)>>> obj2 = Man(“Alex”, 24) >>> obj = obj>>> obj == obj2False>>> obj < obj2True>>> obj >= obj2Traceback (most recent call last): File “”, line 1, in TypeError: ‘>=’ not supported between instances of ‘Man’ and ‘Man’

我們的程式碼適用於 (==) 和 (<),但當我們使用類中未定義的運算子時它不起作用。鑑於我們至少建立了一個運算子 dunder 方法和 __eq__ 方法,@total_ordering 為我們的類生成了、>、=、>= 和更多比較運算子。

讓我們在類的正上方新增我們的裝飾器。

from functools import total_ordering@total_orderingclass Man: def __init__(self, name, age): self。name = name self。age = age def __eq__(self, o): return self。age == o。age def __lt__(self, o): return self。age < o。age

現在再次以互動模式執行程式碼以檢視結果

>>> o = Man(“Vivek”, 20) >>> b = Man(“Alex”, 24) >>> o == b False >>> o >= b False >>> o <= b True

如果你發現我的任何文章對你有幫助或者有用,麻煩點贊或者轉發。 謝謝!