Python functools 模組
雖然《 Python語言參考》描述了Python語言的確切語法和語義,但該庫參考手冊描述了隨Python分發的標準庫。它還描述了Python發行版中通常包含的一些可選元件。Python的標準庫非常廣泛,可提供各種功能。該庫包含內建模組(用C編寫),這些模組提供對系統功能的訪問。
本文將提到有關 functools 模組的內容, 只提到個人認為比較好用的部份 .
@cached_property 轉換函式為屬性值 (python 3.8)
有些類的屬性值為常數, 我們可以一開始就設定, 但有些卻是隨時在變化, 如果我們隨時都要去更新, 在程式碼編寫上很不好處理, 因此, 可以在需要的時候, 以函式來取得結果. 更簡單的方式是呼叫屬性時會自動呼叫函式, 但是程式碼要如何來作到呢? 相信很多人都不清楚要怎麼作到.
下面這個範例, 可以看出來, 只要內容一有更動, 就必須呼叫 get_pets, get_legs 去更新 pets 及 legs 屬性, 相當麻煩.
class Animal():
def __init__(self, cats=0, dogs=0, fishes=0):
self.cats = cats
self.dogs = dogs
self.fishes = fishes
self.get_pets()
self.get_legs()
def get_pets(self):
self.pets = self.cats + self.dogs + self.fishes
def get_legs(self):
self.legs = (self.cats + self.dogs)*3
def set_pets(self, animal, number):
self.__setattr__(animal, number)
self.get_pets()
self.get_legs()
一個好的方式就是使用@cached_property,這樣函式就變成屬性,程式碼又變得簡單, 重要是隻有在用到時才會呼叫該函式。
from functools import cached_property
class Animal():
def __init__(self, cats=0, dogs=0, fishes=0):
self.cats = cats
self.dogs = dogs
self.fishes = fishes
@cached_property
def pets(self):
return self.cats + self.dogs + self.fishes
@cached_property
def legs(self):
return (self.cats + self.dogs)*4
def set_pets(self, animal, number):
self.__setattr__(animal, number)
@lru_cache(maxsize=128) 快取函式的呼叫結果
有時候我們常常會重複呼叫相同引數的函式, 如果每次都要重新處理一次, 速度上就會變得很慢; 因此這裡將呼叫過的函式結果存在快取中, 下次如果再次呼叫, 直接從快取中取出結果就可以了, 這樣會使得速度變得很快.
下面的範例是計算所有N!, N:0~1000的結果, 計算方式採用遞迴, 也就是N! = N * (N-1)!, f1 和f2 兩個函式內容完全一樣, 只不過f2採用了@lru_cache(maxsize=1200), f1 用了約0.2秒, f2 卻只用了約0.001秒, 速度差了200倍啊 !
from functools import lru_cache
from datetime import datetime
import sys
sys.setrecursionlimit(2000)
def f1(n):
if n < 2:
return 1
return n*f1(n-1)
@lru_cache(maxsize=1200)
def f2(n):
if n < 2:
return 1
return n*f2(n-1)
now = datetime.now()
for i in range(1001):
factorial = f1(i)
print(datetime.now()-now)
# 0:00:00.202456
now = datetime.now()
for i in range(1001):
factorial = f2(i)
print(datetime.now()-now)
# 0:00:00.000997
f2.cache_info() # 快取訊息
f2.cache_clear() # 清除快取
partial(func, /, *args, **keywords) 定義一個新的子函式
通常我們建立一個新的函式, 可能帶有一些引數, 當我們常會用到該函式, 而某些引數值是固定的, 用起來程式碼還是那麼長, 而且不容易與原函式區隔; 如果另外要定義新的函式也挺麻煩的. 這裡可以提供一個簡單的方式來使用.
partial 中的 func 為你想簡化的函式, 後面給上指定的引數值, 返回一新的函式.
from functools import partial
int2 = partial(int, base=2)
int8 = partial(int, base=8)
int16 = partial(int, base=16)
int2('101') # int('101', base= 2) => 5
int8('101') # int('101', base= 8) => 65
int16('101') # int('101', base=16) => 257
partialmethod(func, /, args, *keywords) 建立一個類的新函式
這個方法類似 partial, 但用在類的函式定義
from functools import partialmethod
class Animal():
def __init__(self, dogs=0, cats=0):
self.dogs = dogs
self.cats = cats
def set(self, kind, number):
self.__setattr__(kind, number)
set_dogs = partialmethod(set, "dogs")
set_cats = partialmethod(set, "cats")
reduce(function, iterable[, initializer]) 累積處理函式
這個函式本來是python 的內建函式, 後來移到內建模組functools 中, 基本演算法就是取前兩個元素運算, 其結果再與下一個元素再運算, 一直到沒有元素. 比如累加, 累乘, 等等…
from functools import reduce
reduce(lambda x, y:x+y, range(1, 11)) # 1+2+...+10
reduce(lambda x, y:x*y, range(1, 11)) # 1*2*...*10 (10!)
singledispatch 建立一個可修改的通用函式
建立一個可根據第一個引數類別, 分開不同函式處理的通用函式, 內容有點復新, 不好三言兩語就說清楚, 所以只提供以下範例.
from functools import singledispatch
@singledispatch
def print_value(arg):
print(f'argument value is {arg}')
@print_value.register(int)
@print_value.register(float)
def _(arg:int):
print(f'half value of {arg} is {arg/2}')
@print_value.register
def _(arg:complex):
print(f'complex value is {arg}')
def nothing(arg):
print('argument is None')
print_value.register(type(None), nothing)
>>> print_value(10)
half value of 10 is 5.0
>>> print_value(12.5)
half value of 12.5 is 6.25
>>> print_value(3-2j)
complex value is (3-2j)
>>> print("Hello")
Hello
>>> print_value("Hello")
argument value is Hello
>>> print_value(None)
argument is None
>>> print_value.dispatch(float) # 引數為 float 時所呼叫的函式
<function _ at 0x0000000001756700>
>>> print_value.registry # 可以字典的形式來使用
mappingproxy({<class 'complex'>: <function _ at 0x00000000017561F0>,
<class 'float'>: <function _ at 0x0000000001756700>,
<class 'int'>: <function _ at 0x0000000001756700>,
<class 'NoneType'>: <function nothing at 0x0000000001756670>,
<class 'object'>: <function print_value at 0x0000000001746B80>})
本作品採用《CC 協議》,轉載必須註明作者和本文連結