異なる型に対して同じ操作を実装する複数のメソッドで構成されるメソッド。
次の方法でカスタム日付クラス (CustomDate
) を作成する必要があるという要件があります:
タイムスタンプ
年、月、日 (3 つの整数を含むタプル)
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
注: 受信についてはここでは説明しません。 /time stamp が正当であるかどうかは、大まかにタイプを判断するだけです。
さまざまなビルド メソッドを複数のメソッドに分割し、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)。
このライブラリは標準ライブラリの 1 つではないため、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 では、重複する名前のメソッドが定義されている場合、最後のメソッドが前のメソッドを上書きします。
しかし、メタクラスを通じてこの動作を変更できることはご存知ないかもしれません:
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 クラスで複数のコンストラクター メソッドのオーバーロードとジェネリック メソッドを定義する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。