python 泛型函數--singledispatch的使用

@functools.singledispatch:
將一個函數轉變爲單一分派的泛型函數
用 @singledispatch裝飾一個函數,將定義一個泛型函數。注意,我們創建的函數獲得分派的依據是第一個參數的類型:

from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

使用泛函數的register()屬性,重載泛函數的實現。泛函數的register()屬性是一個裝飾器。對於有類型註釋的函數,這個裝飾器將自動匹配第一個參爲該類型的已註冊函數重載泛函數:

@fun.register
def _(arg: int, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register
def _(arg: list, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

當我們調用泛函數fun時,泛函數根據第一個參數的類型來分派相應的函數來重載實現。
第一個參數爲int類型時:

>>> fun(42, verbose=True)
Strength in numbers, eh? 42

第一個參數爲list類型時:

>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam

如果用泛函數的register()屬性進裝飾的函數的參數沒有類型註釋,那麼我們可以在register()裝飾器中明確聲明合適的類型:

@fun.register(complex)
def _(arg, verbose=False):
    if verbose:
        print("Better than complicated.", end=" ")
    print(arg.real, arg.imag)
>>>fun(6+5j, verbose=True)
Better than complicated. 6.0 5.0

爲了能註冊之前存在的函數和匿名函數,register()屬性可以當作功能函數使用。

def nothing(arg, verbose=False):
    print("Nothing.")
    
fun.register(type(None), nothing)
fun.register(int,  lambda x, y, verbose=False: x+y) # 本人添加的,官網沒有這個例子

注:經本人實驗,如果泛函數出兩個可分派的函數,那麼,泛涵數將選擇離調用最近的可分派的函數,即,泛函數將分派在順序上最後定義的函數。

>>> fun(None)
Nothing.
>>>fun(1,2)
3

這個register()屬性將返回一個未被裝飾的函數,這個函數將激活裝飾器的堆棧空間,同時爲它創建一個獨的測試運行單元。

>>>import decimal
>>> @fun.register(float)
... @fun.register(decimal.Decimal)
... def fun_num(arg, verbose=False):
...     if verbose:
...         print("Half of your number:", end=" ")
...     print(arg / 2)
...
>>> fun_num is fun
False

如果泛函數給出的具體類型,沒有對應的註冊函數的實現,那麼泛函數將去尋找更一般化的實現。用@singledispatch裝飾的原函數被註冊了基本類型–object類型,也就是說如果找不到更好的實現,那麼將使用@singledispatch裝飾的原函數:
注:此例由本人提供。

>>>fun(bool,verbose=True)
Let me just say, <class 'bool'>

使用只讀屬性registry,可查看我們都註冊了哪些類型的函數實現

>>> fun.registry.keys()
dict_keys([<class 'object'>, <class 'decimal.Decimal'>, <class 'float'>, <class 'int'>, <class 'list'>, <class 'complex'>, <class 'NoneType'>])
>>> fun.registry
{<class 'object'>: <function fun at 0x00000225F21AC268>, <class 'decimal.Decimal'>: <function fun_num at 0x00000225F2517378>, <class 'float'>: <function fun_num at 0x00000225F2517378>, <class 'int'>: <function <lambda> at 0x00000225F2596488>, <class 'list'>: <function _ at 0x00000225F25172F0>, <class 'complex'>: <function _ at 0x00000225F2596400>, <class 'NoneType'>: <function nothing at 0x00000225F258E400>}
>>> fun.registry[float]
<function fun_num at 0x00000225F2517378>
>>>fun.registry[object]
<function fun at 0x00000225F21AC268>

官方鏈接:https://docs.python.org/3/library/functools.html?highlight=functools wraps#functools.update_wrapper

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章