python基礎之單分派泛函數singledispatch
python單分派泛函數singledispatch
singledispatch 是標準庫 functools 模塊的函數
可以把整體方案拆成多個模塊,甚至可以為你無法修改的類提供專門的函數,使用 @singledispatch 裝飾的函數會變成泛函數
1、 singledispatch :標記處理object類型的基函數
2、各個專門函數使用 @<<base_function>>.register(<<type>>) 裝飾
3、專門函數的名稱無關緊要, _ 是個不錯的選擇,簡單明了
4、為每個需要處理的類型注冊一個函數5、可以疊放多個 register 裝飾器,讓同一個函數支持不同類型
函數中使用
我們來看一個例子了解下:
from functools import singledispatch
from collections import abc
@singledispatch
def show(obj):
print (obj, type(obj), "obj")
#參數字符串
@show.register(str)
def _(text):
print (text, type(text), "str")
#參數int
@show.register(int)
def _(n):
print (n, type(n), "int")
#參數元祖或者字典均可
@show.register(tuple)
@show.register(dict)
def _(tup_dic):
print (tup_dic, type(tup_dic), "int")
show(1)
show("xx")
show([1])
show((1,2,3))
show({"a":"b"})輸出如下:
1 <class 'int'> int
xx <class 'str'> str
[1] <class 'list'> obj
(1, 2, 3) <class 'tuple'> int
{'a': 'b'} <class 'dict'> int
好處是什么呢?類似于java的重載機制,可以在一個類中為同一個方法定義多個重載變體,比在一個函數中使用一長串的 if/elif 好
對象中使用
我們來看一個對象的例子
from functools import singledispatch
class abs:
def type(self,args):
""
class Person(abs):
@singledispatch
def type(self,args):
super().type("",args)
print("我可以接受%s類型的參數%s"%(type(args),args))
@type.register(str)
def _(text):
print("str",text)
@type.register(tuple)
def _(text):
print("tuple", text)
@type.register(list)
@type.register(dict)
def _(text):
print("list or dict", text)
Person.type("safly")
Person.type((1,2,3))
Person.type([1,2,3])
Person.type({"a":1})
Person.type(Person,True)輸出如下:
str safly
tuple (1, 2, 3)
list or dict [1, 2, 3]
list or dict {'a': 1}
我可以接受<class 'bool'>類型的參數True
python的singledispatch裝飾器
最近一直在學習裝飾器的相關知識,學習到了functools中的singledispatch裝飾器,記錄一下
1.Python中不需要使用函數重載的原因
Python中一般是不需要使用函數的重載的。一般的靜態(tài)語言例如C#是支持函數的重載的,為了就是多態(tài)以及代碼的重用。
例如我們現在想要實現一個函數,它可以輸出輸入參數的類型,用C#函數的重載實現的代碼如下
static void GetType(string input)
{
?? ?Console.WriteLine("{0}是string類型", input);
}
static void GetType(int input)
{
?? ?Console.WriteLine("{0}是int類型", input);
}
static void GetType(string[] input)
{
?? ?Console.WriteLine("{0}是數組類型", input);
}
static void GetType(Dictionary<string,string> input)
{
?? ?Console.WriteLine("{0}是字典類型", input);
}此時如果想使用Python實現則不需要使用函數的重載,因為Python本身就是動態(tài)語言,不要在函數的參數中指定參數的類型,可以直接在函數體中判斷變量的類型并且執(zhí)行相應的語句即可:
def print_type(obj):
? ? """輸出參數obj的類型"""
? ? if isinstance(obj, int):
? ? ? ? print(f"{obj}的類型是int")
? ? elif isinstance(obj, str):
? ? ? ? print(f"{obj}的類型是str")
? ? elif isinstance(obj, list):
? ? ? ? print(f"{obj}的類型是list")
? ? elif isinstance(obj, dict):
? ? ? ? print(f"{obj}的類型是dict")
? ? else:
? ? ? ? print(f"{obj}是其他類型")2.Python中的泛函數以及singledispatch
上面簡單的代碼雖然使用Python也實現了功能,但是如果功能再復雜一點則我們需要寫更多的if-elif語句,并且如果函數需要根據參數的類型來執(zhí)行對應的子函數的話此時代碼會更臃腫,并且不利于功能的擴展。
為此,在Python3.4以后在標準庫中functools中加入了singledispatch裝飾器。被singledispatch裝飾的函數稱為泛函數,由此實現的也被稱為單分派泛函數。
其實在Python的標準庫中就存在泛函數,例如len(),它就會根據傳入參數的類型去讀取相應的C結構體中對象的長度。但是在Python3.4以前用戶是沒有辦法實現類似Pythonic的泛函數的。
并且有時候當使用不是自己編寫的或者是無法修改的類的時候,我們需要向其中添加我們自定義的函數,此時就很難做到。但是有了singledispatch之后就會變得很容易。
下面為使用泛函數實現上述代碼功能:
from functools import singledispatch
from collections import abc
import numbers
@singledispatch
def print_type_new(obj): ?
? ? pass
@print_type_new.register(numbers.Integral) ?
def _(n):?
? ? print(f"{n}的類型是Integral")
@print_type_new.register(str)
def _(text):
? ? print(f"{text}的類型是str")
@print_type_new.register(tuple)
@print_type_new.register(abc.MutableSequence)
def _(text):
? ? print(f"{text}的類型是Sequence")
@print_type_new.register(abc.Mapping)
def _(text):
? ? print(f"{text}的類型是Mapping")使用singledispatch的時候,首先需要裝飾一個基函數f(一般參數類型為object),之后需要為各個專門的函數使用類似于@f.register(type)之類的裝飾器,并且要為每個需要特殊處理的類型注冊一個函數,同時為了代碼的兼容性,該專門函數應該盡可能的只處理抽象基類而不要處理具體實現(FluentPython P172)。
注意:
在Python如果需要根據傳入參數的類型來讓函數執(zhí)行不同的操作,則此時應該將函數寫成泛函數,即使用singledispatch裝飾器。
注意singledispatch并不是重載,因為重載還有類似于參數類型相同,但是數量不同,這種類似的情況在Python中只需要使用可變參數*即可以解決。
singledispatch的引入本質是為了讓用戶可以自定義泛函數,以便可以處理在需要根據參數的類型做出相同操作的場合以及為第三方類添加自定義的函數,這一切都是為了提高程序的可擴展性。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Python如何使用標準庫tmpfile庫創(chuàng)建臨時文件
這篇文章主要介紹了Python如何使用標準庫tmpfile庫創(chuàng)建臨時文件問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
python中torch.load中的map_location參數使用
在PyTorch中,torch.load()函數是用于加載保存模型或張量數據的重要工具,map_location參數為我們提供了極大的靈活性,具有一定的參考價值,感興趣的可以了解一下2024-03-03
Python數學建模StatsModels統(tǒng)計回歸之線性回歸示例詳解
這篇文章主要為大家介紹了Python數學建模中StatsModels統(tǒng)計回歸之線性回歸的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10

