python 3.8中的singledispatchmethod和类方法装饰器

lag*_*lix 6 python python-3.x python-decorators

我正在尝试使用 python 3.8 的新功能之一(当前使用 3.8.3)。按照文档,我尝试了文档中提供的示例:

from functools import singledispatchmethod
class Negator:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    @classmethod
    def _(cls, arg: int):
        return -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool):
        return not arg

Negator.neg(1)
Run Code Online (Sandbox Code Playgroud)

但是,这会产生以下错误:

...
TypeError: Invalid first argument to `register()`: <classmethod object at 0x7fb9d31b2460>. Use either `@register(some_class)` or plain `@register` on an annotated function.
Run Code Online (Sandbox Code Playgroud)

如何创建泛型类方法?我的示例中是否缺少某些内容?

更新:

我已经阅读了 Aashish A 的回答,这似乎是一个持续存在的问题。我设法通过以下方式解决了我的问题。

...
TypeError: Invalid first argument to `register()`: <classmethod object at 0x7fb9d31b2460>. Use either `@register(some_class)` or plain `@register` on an annotated function.
Run Code Online (Sandbox Code Playgroud)

这似乎适用于 3.8.1 和 3.8.3 版,但似乎不应该,因为我没有在 undescore 函数上使用 staticmethod 装饰器。这确实适用于 classmethods,即使问题似乎表明相反。

请记住,如果您使用的是 IDE,则 linter 不会对这种方法感到满意,从而引发很多错误。

小智 6

这似乎是本期记录的 functools 库中的一个错误。


Ale*_*ood 5

此错误在 Python >= 3.9.8 中不再存在。在 Python 3.9.8 中,代码singledispatchmethod 已经过调整,以确保它可以与类型注释和classmethods/ staticmethods 一起使用。然而,在 Python 3.10+ 中,该错误作为更改classmethods 和staticmethods 相对于__annotations__它们所包装的函数的属性的行为方式的副产品而得到解决。

在 Python 3.9 中:

>>> x = lambda y: y
>>> x.__annotations__ = {'y': int}
>>> c = classmethod(x)
>>> c.__annotations__
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    c.__annotations__
AttributeError: 'classmethod' object has no attribute '__annotations__'
Run Code Online (Sandbox Code Playgroud)

在 Python 3.10+ 中:

>>> x = lambda y: y
>>> x.__annotations__ = {'y': int}
>>> c = classmethod(x)
>>> c.__annotations__
{'y': <class 'int'>}
Run Code Online (Sandbox Code Playgroud)

此更改似乎解决了 的问题singledispatchmethod,这意味着在 Python 3.9.8 和 Python 3.10+ 中,以下代码可以正常工作:

from functools import singledispatchmethod

class Negator:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError(f"Cannot negate object of type '{type(arg).__name__}'")

    @neg.register
    @classmethod
    def _(cls, arg: int) -> int:
        return -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool) -> bool:
        return not arg

print(Negator.neg(1))
print(Negator.neg(False))
Run Code Online (Sandbox Code Playgroud)