python基礎(chǔ)之單分派泛函數(shù)singledispatch
python單分派泛函數(shù)singledispatch
singledispatch
是標(biāo)準(zhǔn)庫(kù) functools
模塊的函數(shù)
可以把整體方案拆成多個(gè)模塊,甚至可以為你無(wú)法修改的類提供專門(mén)的函數(shù),使用 @singledispatch
裝飾的函數(shù)會(huì)變成泛函數(shù)
1、 singledispatch
:標(biāo)記處理object類型的基函數(shù)
2、各個(gè)專門(mén)函數(shù)使用 @<<base_function>>.register(<<type>>)
裝飾
3、專門(mén)函數(shù)的名稱無(wú)關(guān)緊要, _
是個(gè)不錯(cuò)的選擇,簡(jiǎn)單明了
4、為每個(gè)需要處理的類型注冊(cè)一個(gè)函數(shù)5、可以疊放多個(gè) register
裝飾器,讓同一個(gè)函數(shù)支持不同類型
函數(shù)中使用
我們來(lái)看一個(gè)例子了解下:
from functools import singledispatch from collections import abc @singledispatch def show(obj): print (obj, type(obj), "obj") #參數(shù)字符串 @show.register(str) def _(text): print (text, type(text), "str") #參數(shù)int @show.register(int) def _(n): print (n, type(n), "int") #參數(shù)元祖或者字典均可 @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的重載機(jī)制,可以在一個(gè)類中為同一個(gè)方法定義多個(gè)重載變體,比在一個(gè)函數(shù)中使用一長(zhǎng)串的 if/elif
好
對(duì)象中使用
我們來(lái)看一個(gè)對(duì)象的例子
from functools import singledispatch class abs: def type(self,args): "" class Person(abs): @singledispatch def type(self,args): super().type("",args) print("我可以接受%s類型的參數(shù)%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'>類型的參數(shù)True
python的singledispatch裝飾器
最近一直在學(xué)習(xí)裝飾器的相關(guān)知識(shí),學(xué)習(xí)到了functools中的singledispatch裝飾器,記錄一下
1.Python中不需要使用函數(shù)重載的原因
Python中一般是不需要使用函數(shù)的重載的。一般的靜態(tài)語(yǔ)言例如C#是支持函數(shù)的重載的,為了就是多態(tài)以及代碼的重用。
例如我們現(xiàn)在想要實(shí)現(xiàn)一個(gè)函數(shù),它可以輸出輸入?yún)?shù)的類型,用C#函數(shù)的重載實(shí)現(xiàn)的代碼如下
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}是數(shù)組類型", input); } static void GetType(Dictionary<string,string> input) { ?? ?Console.WriteLine("{0}是字典類型", input); }
此時(shí)如果想使用Python實(shí)現(xiàn)則不需要使用函數(shù)的重載,因?yàn)镻ython本身就是動(dòng)態(tài)語(yǔ)言,不要在函數(shù)的參數(shù)中指定參數(shù)的類型,可以直接在函數(shù)體中判斷變量的類型并且執(zhí)行相應(yīng)的語(yǔ)句即可:
def print_type(obj): ? ? """輸出參數(shù)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中的泛函數(shù)以及singledispatch
上面簡(jiǎn)單的代碼雖然使用Python也實(shí)現(xiàn)了功能,但是如果功能再?gòu)?fù)雜一點(diǎn)則我們需要寫(xiě)更多的if-elif語(yǔ)句,并且如果函數(shù)需要根據(jù)參數(shù)的類型來(lái)執(zhí)行對(duì)應(yīng)的子函數(shù)的話此時(shí)代碼會(huì)更臃腫,并且不利于功能的擴(kuò)展。
為此,在Python3.4以后在標(biāo)準(zhǔn)庫(kù)中functools中加入了singledispatch裝飾器。被singledispatch裝飾的函數(shù)稱為泛函數(shù),由此實(shí)現(xiàn)的也被稱為單分派泛函數(shù)。
其實(shí)在Python的標(biāo)準(zhǔn)庫(kù)中就存在泛函數(shù),例如len(),它就會(huì)根據(jù)傳入?yún)?shù)的類型去讀取相應(yīng)的C結(jié)構(gòu)體中對(duì)象的長(zhǎng)度。但是在Python3.4以前用戶是沒(méi)有辦法實(shí)現(xiàn)類似Pythonic的泛函數(shù)的。
并且有時(shí)候當(dāng)使用不是自己編寫(xiě)的或者是無(wú)法修改的類的時(shí)候,我們需要向其中添加我們自定義的函數(shù),此時(shí)就很難做到。但是有了singledispatch之后就會(huì)變得很容易。
下面為使用泛函數(shù)實(shí)現(xiàn)上述代碼功能:
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的時(shí)候,首先需要裝飾一個(gè)基函數(shù)f(一般參數(shù)類型為object),之后需要為各個(gè)專門(mén)的函數(shù)使用類似于@f.register(type)之類的裝飾器,并且要為每個(gè)需要特殊處理的類型注冊(cè)一個(gè)函數(shù),同時(shí)為了代碼的兼容性,該專門(mén)函數(shù)應(yīng)該盡可能的只處理抽象基類而不要處理具體實(shí)現(xiàn)(FluentPython P172)。
注意:
在Python如果需要根據(jù)傳入?yún)?shù)的類型來(lái)讓函數(shù)執(zhí)行不同的操作,則此時(shí)應(yīng)該將函數(shù)寫(xiě)成泛函數(shù),即使用singledispatch裝飾器。
注意singledispatch并不是重載,因?yàn)橹剌d還有類似于參數(shù)類型相同,但是數(shù)量不同,這種類似的情況在Python中只需要使用可變參數(shù)*即可以解決。
singledispatch的引入本質(zhì)是為了讓用戶可以自定義泛函數(shù),以便可以處理在需要根據(jù)參數(shù)的類型做出相同操作的場(chǎng)合以及為第三方類添加自定義的函數(shù),這一切都是為了提高程序的可擴(kuò)展性。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Tensorflow矩陣運(yùn)算實(shí)例(矩陣相乘,點(diǎn)乘,行/列累加)
今天小編就為大家分享一篇Tensorflow矩陣運(yùn)算實(shí)例(矩陣相乘,點(diǎn)乘,行/列累加),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02用pandas劃分?jǐn)?shù)據(jù)集實(shí)現(xiàn)訓(xùn)練集和測(cè)試集
這篇文章主要介紹了用pandas劃分?jǐn)?shù)據(jù)集實(shí)現(xiàn)訓(xùn)練集和測(cè)試集,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Python實(shí)現(xiàn)簡(jiǎn)單的文件傳輸與MySQL備份的腳本分享
這篇文章主要介紹了Python實(shí)現(xiàn)簡(jiǎn)單的文件傳輸與MySQL備份的腳本分享,用到了socket與tarfile模塊,需要的朋友可以參考下2016-01-01Python實(shí)現(xiàn)的排列組合計(jì)算操作示例
這篇文章主要介紹了Python實(shí)現(xiàn)的排列組合計(jì)算操作,涉及Python數(shù)學(xué)運(yùn)算的相關(guān)函數(shù)與使用技巧,需要的朋友可以參考下2017-10-10Pandas常用的讀取和保存數(shù)據(jù)的函數(shù)使用(csv,mysql,json,excel)
本文主要介紹了Pandas常用的讀取和保存數(shù)據(jù)的函數(shù)使用,主要包括csv,mysql,json,excel這幾種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2022-01-01