我想把functools模块中的单个调度方法和abc中的 我已经用其他方法解决了这个问题,但我很好奇这个问题是否有答案class Abstract(metaclass=ABCMeta):
...
...
...
@singledispatchmethod
@abstractmethod
def apply(self, element:str):
raise NotImplementedError
@apply.register
@abstractmethod
def _(self, element: int):
raise NotImplementedError
class A(Abstract):
def apply(self, element: str):
print(f'this is string -> {element}')
def _(self, element: int):
print(f'this is intiger -> {element}')
>>>instance = A()
>>>instance.apply(2)
#will return (this is string -> 2)
实现
我很想知道这一点,因为我刚刚参与了一个项目,我想拥有这个功能,因为我正在从C#移植一个应用程序,在那里我可以简单地使用重载。我用两种不同的方法来处理这种行为:
派生类调度
这是我需要的版本,它使dispatchmethod变得抽象,因此每个派生类也必须定义一个具有相同签名的具体dispatchmethod(重要:这些现在是单独的分派方法,可以提供不同的注册(。还要注意,因为这些都是具体的,所以每个派生类都有自己的分派,并且DoerBase
中的函数永远不会被调用(因为它只是抽象的(。
from abc import ABC, abstractmethod
from functools import singledispatchmethod
from typing import Any
from typing_extensions import override
class DoerBase(ABC):
@singledispatchmethod
@abstractmethod
def do_something(self, arg: Any) -> None: ...
class IntDoer(DoerBase):
@singledispatchmethod
@override
def do_something(self, arg: Any) -> None:
raise NotImplementedError(f"This {type(self).__name__} cannot do anything with a {type(arg).__name__}!")
@do_something.register
def _(self, arg: int):
print("The number", arg, "is half of", 2 * arg)
class StringDoer(DoerBase):
@singledispatchmethod
@override
def do_something(self, arg: Any) -> None:
raise NotImplementedError(f"This {type(self).__name__} cannot do anything with a {type(arg).__name__}!")
@do_something.register
def _(self, arg: str):
print("I can print this string for you:", arg)
def main():
int_doer = IntDoer()
string_doer = StringDoer()
int_doer.do_something(321)
string_doer.do_something("Hello")
# This IntDoer cannot do anything with a str!
# int_doer.do_something("Hello")
# This StringDoer cannot do anything with a int!
# string_doer.do_something(321)
if __name__ == "__main__":
main()
基类调度
与示例中的版本更相似的版本在基类中声明已注册的调度类型(上面的方法为每个派生类声明注册(。现在,每个基类都必须覆盖抽象调度注册。我能够通过从注册的调度处理程序调用一个抽象方法来重新创建这种行为,而不是试图使调度处理程序本身抽象。
from abc import ABCMeta, abstractmethod
from functools import singledispatchmethod
from typing import Any
class ABase(metaclass=ABCMeta):
@singledispatchmethod
def apply(self, element: Any) -> None: ...
@apply.register
def _(self, element: int): return self.apply_int(element)
@abstractmethod
def apply_int(self, element: int) -> None: ...
class A(ABase):
def apply_int(self, element: int):
print("I applied the number", element)
class B(ABase): pass
instance = A()
instance.apply(2)
#will print "I applied the number 2"
# b_instance = B()
# TypeError: Can't instantiate abstract class B with abstract method apply_int
结论:
- 在派生类上,您肯定还需要提供
@singledispatchmethod
装饰器 - 似乎无法将
@method.register
装饰器与@abstractmethod
装饰器组合在一起 - 您可以通过小心地放置
@abstractmethod
装饰器来规避这种行为 - 上面两个实现的行为不同:一个声明类有一个分派方法,每个子类都必须声明和注册,而另一个实现定义所有个子类必须完成的分派注册