您好,登錄后才能下訂單哦!
今天小編給大家分享一下Python怎么定義多個構造器方法重載和泛方法的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
由多個方法組成的方法,這些方法為不同的類型實現相同的操作。
現在有個需求,需要你通過以下幾種方式創建一個自定義的日期類(CustomDate
):
時間戳
年、月、日(包含三個整數的元組)
ISO 格式的字符串
Datetime
類
from datetime import date, datetime class CustomDate: def __init__(self, arg): if isinstance(arg, (int, float)): self.__date = date.fromtimestamp(arg) elif isinstance(arg, tuple) and len(arg) == 3 and all(map(lambda x: isinstance(x, int), arg): self.__date = date(*arg) elif isinstance(arg, str): self.__date = date.fromisoformat(arg) elif isinstance(arg, datetime): self.__date = datetime.date() else: raise TypeError("could not create instance from " + type(arg).__name__) @property def date(): return self.__date
注:這里暫不討論傳入的日期/時間戳合不合法,僅僅只對類型做大致判斷。
我們可以將不同的構建方式拆分為多個方法,并利用 functools
中的 singledispatchmethod
裝飾器來根據傳入的參數類型決定調用哪個方法。
from datetime import date, datetime from functools import singledispatchmethod class CustomDate: @singledispatchmethod def __init__(self, arg): raise TypeError("could not create instance from " + type(arg).__name__) @__init__.register(int) @__init__.register(float) def __from_timestamp(self, arg): self.__date = date.fromtimestamp(arg) @__init__.register(tuple) def __from_tuple(self, arg): if len(arg) == 3 and all(map(lambda x: isinstance(x, int), arg)): self.__date = date(*arg) else: raise ValueError("could not create instance from a malformed tuple") @__init__.register(str) def __from_isoformat(self, arg): self.__date = date.fromisoformat(arg) @__init__.register(datetime) def __from_datetime(self, arg): self.__date = arg.date() @property def date(self): return self.__date
這樣一來,我們便能將每種參數類型的初始化獨立成一個個的方法了。
在調用期間應該使用哪個方法實現由分派算法決定。如果該算法只基于單個參數的類型來決定使用哪個方法實現,則稱其為單分派。
singledispatchmethod
就是就是單分派的。也就是說,只有第一個參數會作為考量。這在實際業務中是遠遠不足的。
然而,如上,對元組中元素類型判斷還是需要我們用 if
/else
實現。也就是說,我們不能使用 typing.Tuple[int, int, int]
。
作為一種折中的方案,或許我們可以定義一個 ThreeIntTuple
類來對其進行限定,將這些判斷從 CustomDate
類中隔離開來。
這里僅提供一個思路讓大家參考,我就不實現了(因為我們有更好的方式 xD)。
這個庫不是標準庫之一,需要通過 pip 安裝:
pip install multimethod
multimethod
采用的是多分派算法,能更好地滿足更復雜的場景。此外,該庫對 typing
中的類型也有不錯的支持。
回到上面的問題,我們可以這么改進:
使用 multimethod
方法來替代 singledispatchmethod
;
使用 Tuple[int, int, int]
來替代 tuple
,不再需要手動校驗元組的長度和元素類型了;
from datetime import date, datetime from typing import Tuple, Union from multimethod import multimethod class CustomDate: @multimethod def __init__(self, arg): raise TypeError("could not create instance from " + type(arg).__name__) @__init__.register def __from_timestamp(self, arg: Union[int, float]): self.__date = date.fromtimestamp(arg) @__init__.register def __from_tuple(self, arg: Tuple[int, int, int]): self.__date = date(*arg) @__init__.register def __from_isoformat(self, arg: str): self.__date = date.fromisoformat(arg) @__init__.register def __from_datetime(self, arg: datetime): self.__date = arg.date() @property def date(self): return self.__date
在此之前,先問大家一個簡單的問題(這跟我們之后的內容有很大的聯系):
class A: def a(self): print(1) def a(self): print(2) A().a()
以上這段代碼會輸出什么?還是會拋出錯誤?
輸出 2
。
在 Python 中,如果定義了重名的方法,最后一個方法是會覆蓋掉之前的方法的。
但你或許不知,我們可以通過元類(metaclass)來改變這一行為:
class MetaA(type): class __prepare__(dict): def __init__(*args): pass def __setitem__(self, key, value): if self.get('a'): # Line 7 super().__setitem__('b', value) # Line 8 else: super().__setitem__(key, value) class A(metaclass=MetaA): def a(self): print(1) def a(self): print(2) A().a() # => 1 A().b() # => 2 # Line 22
在第 7 和第 8 行,我們將重名的 a
方法改名為 b
,并在第 22 行成功地調用它了。
multimethod
的維護者們很好地運用了這一點,對重名的方法進行了處理,以達到一種“特殊的效果”。
回到正題,我們可以做出如下改進:
將 multimethod.multidata
設置為 CustomDate
類的元類;
將所有方法命名為 __init__
。
from datetime import date, datetime from typing import Tuple, Union from multimethod import multimeta class CustomDate(metaclass=multimeta): def __init__(self, arg: Union[int, float]): self.__date = date.fromtimestamp(arg) def __init__(self, arg: Tuple[int, int, int]): self.__date = date(*arg) def __init__(self, arg: str): self.__date = date.fromisoformat(arg) def __init__(self, arg: datetime): self.__date = arg.date() def __init__(self, arg): raise TypeError("could not create instance from " + type(arg).__name__) @property def date(self): return self.__date
從效果上來看,這完全和靜態語言的方法重載一模一樣!
以上就是“Python怎么定義多個構造器方法重載和泛方法”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。