【问题标题】:singledispatchmethod and class method decorators in python 3.8python 3.8 中的 singledispatchmethod 和类方法装饰器
【发布时间】:2020-10-23 01:54:27
【问题描述】:

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

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)

然而,这会产生以下错误:

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

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

更新:

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

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

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

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

print(Negator.neg(False))
print(Negator.neg(-1))

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

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

【问题讨论】:

  • 术语说明:您定义的是类方法,而不是静态方法。 Python 将两者区分开来,因为类方法接收类作为隐式参数,而静态方法不接收隐式参数(对于这两种类型的方法,无论是从类调用还是从类的实例调用,行为都是相同的)。
  • 感谢您的澄清,我错了,可能是因为我也在使用 staticmethod 装饰器进行测试,但效果不佳。

标签: python python-3.x python-decorators


【解决方案1】:

解决方法是修补 singledispatchmethod.register()

from functools import singledispatchmethod

def _register(self, cls, method=None):
    if hasattr(cls, '__func__'):
        setattr(cls, '__annotations__', cls.__func__.__annotations__)
    return self.dispatcher.register(cls, func=method)

singledispatchmethod.register = _register

【讨论】:

    【解决方案2】:

    Python >= 3.9.8 中不再存在此错误。在 Python 3.9.8 中,singledispatchmethodhas been tweaked 的代码确保它与类型注释和 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__'
    

    在 Python 3.10 中:

    >>> x = lambda y: y
    >>> x.__annotations__ = {'y': int}
    >>> c = classmethod(x)
    >>> c.__annotations__
    {'y': <class 'int'>}
    

    此更改似乎解决了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))
    

    【讨论】:

      【解决方案3】:

      这似乎是 this issue 中记录的 functools 库中的一个错误。

      【讨论】:

        猜你喜欢
        • 2012-02-09
        • 1970-01-01
        • 2018-07-14
        • 1970-01-01
        • 2010-10-16
        • 2012-09-11
        • 2014-01-14
        • 2021-04-12
        • 2020-01-11
        相关资源
        最近更新 更多