python?泛型函數(shù)--singledispatch的使用解讀
@functools.singledispatch
將一個函數(shù)轉(zhuǎn)變?yōu)閱我环峙傻姆盒秃瘮?shù)
用 @singledispatch裝飾一個函數(shù),將定義一個泛型函數(shù)。注意,我們創(chuàng)建的函數(shù)獲得分派的依據(jù)是第一個參數(shù)的類型:
from functools import singledispatch @singledispatch def fun(arg, verbose=False): ? ? if verbose: ? ? ? ? print("Let me just say,", end=" ") ? ? print(arg)
使用泛函數(shù)的register()屬性,重載泛函數(shù)的實現(xiàn)。泛函數(shù)的register()屬性是一個裝飾器。對于有類型注釋的函數(shù),這個裝飾器將自動匹配第一個參為該類型的已注冊函數(shù)重載泛函數(shù):
@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)
當(dāng)我們調(diào)用泛函數(shù)fun時,泛函數(shù)根據(jù)第一個參數(shù)的類型來分派相應(yīng)的函數(shù)來重載實現(xiàn)。
第一個參數(shù)為int類型時:
>>> fun(42, verbose=True) Strength in numbers, eh? 42
第一個參數(shù)為list類型時:
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True) Enumerate this: 0 spam 1 spam 2 eggs 3 spam
如果用泛函數(shù)的register()屬性進裝飾的函數(shù)的參數(shù)沒有類型注釋,那么我們可以在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
為了能注冊之前存在的函數(shù)和匿名函數(shù),register()屬性可以當(dāng)作功能函數(shù)使用。
def nothing(arg, verbose=False): ? ? print("Nothing.") ? ?? fun.register(type(None), nothing) fun.register(int, ?lambda x, y, verbose=False: x+y) # 本人添加的,官網(wǎng)沒有這個例子
注:經(jīng)本人實驗,如果泛函數(shù)出兩個可分派的函數(shù),那么,泛涵數(shù)將選擇離調(diào)用最近的可分派的函數(shù),即,泛函數(shù)將分派在順序上最后定義的函數(shù)。
>>> fun(None) Nothing. >>>fun(1,2) 3
這個register()屬性將返回一個未被裝飾的函數(shù),這個函數(shù)將激活裝飾器的堆??臻g,同時為它創(chuàng)建一個獨的測試運行單元。
>>>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
如果泛函數(shù)給出的具體類型,沒有對應(yīng)的注冊函數(shù)的實現(xiàn),那么泛函數(shù)將去尋找更一般化的實現(xiàn)。用@singledispatch裝飾的原函數(shù)被注冊了基本類型–object類型,也就是說如果找不到更好的實現(xiàn),那么將使用@singledispatch裝飾的原函數(shù):
注:此例由本人提供。
>>>fun(bool,verbose=True) Let me just say, <class 'bool'>
使用只讀屬性registry,可查看我們都注冊了哪些類型的函數(shù)實現(xiàn)
>>> 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
singledispatch實現(xiàn)單分派泛函數(shù)和多分派泛函數(shù)
本次的主題是逐漸闖入無人區(qū)的泛型?。。?/strong>
說到泛型,學(xué)過java的一定不陌生,泛型的本質(zhì)是參數(shù)化類型,也就是所操作的數(shù)據(jù)類型被指定為一個參數(shù)。但是,學(xué)過python的大家是否了解過這部分,或者是使用呢?
那么,python該如何實現(xiàn)泛型呢?
你別說,還真有一個庫可以實現(xiàn)!
我們首先導(dǎo)入singledispatch所在的庫:
from functools import singledispatch
這個庫只能針對函數(shù)的第一個參數(shù)進行泛型指定!
先指定一個主函數(shù)用singledispatch修飾一下,作為一個base, 之后在定義一些“子函數(shù)”用 @主函數(shù)名.register作為修飾器,并傳入一個參數(shù)作為“子函數(shù)”第一個參數(shù)的類型的判斷(只能傳入一個參數(shù))(這個參數(shù)就是“子函數(shù)”第一個參數(shù)的類型,也是主函數(shù)第一個參數(shù)的類型)。(注意:這里的子函數(shù)就是那個_,應(yīng)為這個子函數(shù)只在泛函數(shù)里面會使用到,所以我們干脆不指定他的名字QAQ, 函數(shù)的參數(shù)也和主函數(shù)一樣)
但是,這樣局限性也太大了,根本沒有什么實際用處,我們還要推廣到多分派泛函數(shù)?。?!
多分派泛函數(shù)的實現(xiàn):(因為python只能對第一個參數(shù)進行判斷泛型,所以我們需要添加一些自己的代碼實現(xiàn)多分派反函數(shù))
我們在單分派的基礎(chǔ)上使用isinstance進行了判斷,保證其他參數(shù)的類型的一致性。
以上的多分派泛函數(shù)也可以這樣寫:
每一個子函數(shù)使用了兩個修飾器,但是這兩個修飾器都是針對第一個參數(shù)的。
?。?!你以為這樣就完結(jié)了???
python 3.5 推出了新特性——參數(shù)后面加一個冒號和函數(shù)后面加一個->的用法:
(冒號是指該參數(shù)應(yīng)該的參數(shù)類型,箭頭是指函數(shù)應(yīng)該的返回值)
他也是指定了參數(shù)的類型,但是呢,就算你傳入的類型和冒號后面的不一樣,也并不會報錯(除非你有語法錯誤),所以,這并不是泛型。
但是他和泛型也有些關(guān)系,這涉及到了register修飾器的第二個用法:
省略了register的參數(shù),而使用’:‘符號進行指定。
!??!完結(jié)?。?!
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Pycharm學(xué)習(xí)教程(7)虛擬機VM的配置教程
這篇文章主要為大家詳細介紹了最全的Pycharm學(xué)習(xí)教程第七篇,Python快捷鍵相關(guān)設(shè)置,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Python一行代碼實現(xiàn)ChatGPT接入微信機器人
這篇文章主要為大家介紹了Python一行代碼實現(xiàn)ChatGPT接入微信機器人示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03python 將md5轉(zhuǎn)為16字節(jié)的方法
今天小編就為大家分享一篇python 將md5轉(zhuǎn)為16字節(jié)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05