具有类型的单一调度方法

Posted

技术标签:

【中文标题】具有类型的单一调度方法【英文标题】:singledispatchmethod with typing types 【发布时间】:2020-10-23 07:32:35 【问题描述】:

在 Python 3.8 中,创建了一个用于创建多态函数的新装饰器 @singledispatchmethod,它根据提供的类型提示将 Python 重定向到您的方法的正确实现。

但是,我似乎无法使用打字模块中的复杂类型,请您告诉我我的示例有什么问题吗?

from typing import List
from functools import singledispatchmethod
class test:
    @singledispatchmethod
    def a(self, a):
        return NotImplemented

    @a.register
    def _(self, a : str):
        print(type(a))
    
    # Uncomment to run the example
    # @a.register
    # def _(self, a: List[str]):
    #    print(type(a))

    def b(self, b: List[str]):
        print(type(b))

test().a(["A"])
test().b(["A"])

如果第二个下划线函数的注释被取消注释,a函数会出现以下错误,即使b函数没有发生这种错误:

TypeError: Invalid annotation for 'a'. typing.List[str] is not a class.

我做错了什么?

【问题讨论】:

看起来像singledispatch 定义中的另一个错误。 register 方法显式检查参数的注解是否是 type 的实例,而类型提示则不是这样。 @chepner 你知道这是否在任何地方的错误跟踪器中,还是 OP 刚刚发现了一个新的 stdlib 错误? 也许是这个? bugs.python.org/issue34498 我认为这个问题涵盖了这一点。函数注释一直被允许是任意表达式,即使它们实际上总是类型。 PEP-484 开启了要求注解为类类型的道路,但typing 模块是引入非类型类对象的第一件事。 singledispatch 似乎跟不上这些发展。 感谢您的参考,至少我知道在这种情况下我没有做错任何事情 【参考方案1】:

从 cmets 看来,可能有 potential changes 到 singledispatchmethod,但如果您对可用的变通方法感兴趣,以允许调度和打字工作,这里有一些选项:

    使用实际类list 注册重载,然后为重载提供更具体的类型提示 (see related answer)。 使用像 multimethod 这样的项目来处理更多参数化的 typing 类型。

选项 1:注册 list 并添加更具体的重载类型提示

from functools import singledispatchmethod
from typing import List, overload


class test:
    @singledispatchmethod
    def overloaded_a(self, a):
        return NotImplemented

    @overloaded_a.register
    def _(self, a: str):
        print(type(a))

    @overloaded_a.register
    def _from_list(self, a: list):
        print(type(a))

    @overload
    def a(self, a: str): ...

    @overload
    def a(self, a: List[str]): ...

    def a(self, *args, **kwargs):
        return self.overloaded_a(*args, **kwargs)

    def b(self, b: List[str]):
        print(type(b))


test().a(["A"])
test().b(["A"])
test().a("this")
# test().a([34])  # mypy error: List item 0 has incompatible type "int"; expected "str"
# test().b("this")  # mypy error: Argument 1 to "b" of "test" has incompatible type "str"; expected "List[str]"

选项2:使用multimethod

from typing import List

from multimethod import multimethod as singledispatchmethod


class test:
    @singledispatchmethod
    def a(self, a):
        return NotImplemented

    @a.register
    def _(self, a: str):
        print("str")
        print(type(a))

    @a.register
    def _from_str_list(self, a: List[str]):
        print("str list")
        print(type(a))

    @a.register
    def _from_int_list(self, a: List[int]):
        print("int list")
        print(type(a))

    def b(self, b: List[str]):
        print(type(b))


test().a(["A"])
test().a([3])
test().b(["A"])
# test().b(3) # mypy error: Argument 1 to "b" of "test" has incompatible type "int"; expected "List[str]"

【讨论】:

以上是关于具有类型的单一调度方法的主要内容,如果未能解决你的问题,请参考以下文章

安卓。具有单一活动方法的 BottomNavigationView

从零开始的Java开发1-3-1 Java面向对象:创建类实例化对象单一职责原则newthis构造方法

设计和构建具有大量重复任务的任务调度系统的好方法是啥?

C ++参数包,仅限于具有单一类型的实例?

如何在不相关的类型上实现动态多态(运行时调用调度)?

有没有更好的方法来检查多个变量是Python中的单一类型?