Mypy:帮助装饰器注释多个调度的泛型

时间:2019-12-06 10:30:35

标签: python mypy

我想将我的函数放在某个注册表中,并根据提供的一些参数(多个调度的版本)选择所需的函数。

这是示例代码:

method_registry = {}


def accepts(cls):
    """ A decorator that registers functions on some registry"""
    def register(func):
        method_registry[cls] = func
        return func

    return register


def handle(obj):
    """ Pick a function that corresponds to the provided object class and run it """
    handler = method_registry[obj.__class__]
    handler(obj)

现在,我打算如何使用它:


class Dog:
    def bark(self):
        print('bark')

class Cat:
    def meow(self):
        print('meow')


@accepts(Dog)
def handle_dog(obj):
    obj.bark()


@accepts(Cat)
def handle_cat(obj):
    obj.meow()


# Here comes multiple dispatch
handle(Dog())
handle(Cat())

现在,这一切正常,但是当我尝试为mypy注释函数时,我必须两次键入CatDog

@accepts(Dog)
def handle_dog(obj: Dog) -> None:
    obj.bark()


@accepts(Cat)
def handle_cat(obj: Cat) -> None:
    obj.meow()

因此,我猜测必须有一种创建通用类型的方法,该类型可以为我注释obj-s而无需重复代码。

但是我似乎无法完成这项工作。

我的尝试是这样的:

V = TypeVar('V')

def accepts(cls: Type[V]) -> Callable[[Callable], Callable[[V], None]]:

    def register(func: Callable) -> Callable[[V], None]:
        method_registry[cls] = func
        return func

    return register

但这没有帮助:

@accepts(Dog)
def handle_dog(obj) -> None:
    reveal_type(obj)  # Revealed type is 'Any'
    obj.bark()

有没有办法使这项工作成功?

1 个答案:

答案 0 :(得分:0)

目前的代码要求您指定两次类型:一次用于 MyPy,一次用于 accept。理论上,这些类型可能不同。

如果您想让它们都使用相同的值,则需要更改其中之一。 您更改 accepts 方法比为 MyPy 编写插件要容易得多。它可以通过检查函数的 __annotations__ 字段来使用相同的注释

def accepts():
    def register( func ):
        cls = func.__annotations__["obj"]
        method_registry[cls] = func
        return func
    
    return register

...

@accepts()
def handle_dog( obj: Dog ):
    obj.bark()

@accepts()
def handle_cat( obj: Cat ):
    obj.meow()