網站首頁 編程語言 正文
@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
singledispatch實現單分派泛函數和多分派泛函數
本次的主題是逐漸闖入無人區的泛型!!!
說到泛型,學過java的一定不陌生,泛型的本質是參數化類型,也就是所操作的數據類型被指定為一個參數。但是,學過python的大家是否了解過這部分,或者是使用呢?
那么,python該如何實現泛型呢?
你別說,還真有一個庫可以實現!
我們首先導入singledispatch所在的庫:
from functools import singledispatch
這個庫只能針對函數的第一個參數進行泛型指定!
先指定一個主函數用singledispatch修飾一下,作為一個base, 之后在定義一些“子函數”用 @主函數名.register作為修飾器,并傳入一個參數作為“子函數”第一個參數的類型的判斷(只能傳入一個參數)(這個參數就是“子函數”第一個參數的類型,也是主函數第一個參數的類型)。(注意:這里的子函數就是那個_,應為這個子函數只在泛函數里面會使用到,所以我們干脆不指定他的名字QAQ, 函數的參數也和主函數一樣)
但是,這樣局限性也太大了,根本沒有什么實際用處,我們還要推廣到多分派泛函數!!!
多分派泛函數的實現:(因為python只能對第一個參數進行判斷泛型,所以我們需要添加一些自己的代碼實現多分派反函數)
?我們在單分派的基礎上使用isinstance進行了判斷,保證其他參數的類型的一致性。
以上的多分派泛函數也可以這樣寫:
?每一個子函數使用了兩個修飾器,但是這兩個修飾器都是針對第一個參數的。
!!!你以為這樣就完結了???
python 3.5? 推出了新特性——參數后面加一個冒號和函數后面加一個->的用法:
(冒號是指該參數應該的參數類型,箭頭是指函數應該的返回值)
他也是指定了參數的類型,但是呢,就算你傳入的類型和冒號后面的不一樣,也并不會報錯(除非你有語法錯誤),所以,這并不是泛型。
但是他和泛型也有些關系,這涉及到了register修飾器的第二個用法:
?省略了register的參數,而使用’:‘符號進行指定。
!!!完結!!!
原文鏈接:https://blog.csdn.net/zjz155/article/details/88593291
相關推薦
- 2023-02-01 C語言中聯合體與共用體和枚舉使用語法示例_C 語言
- 2021-11-26 Linux下查看IP地址不顯示解決辦法_Linux
- 2022-10-17 Kotlin編程基礎數據類型示例詳解_Android
- 2022-11-03 一文詳解C++子類函數為什么不能重載父類函數_C 語言
- 2022-04-10 微信小程序音樂播放器
- 2022-04-25 一篇文章帶你了解C語言的文件操作_C 語言
- 2023-04-19 vscode配置ESlint
- 2022-09-06 C#面向對象編程中接口隔離原則的示例詳解_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支