【问题标题】:mypy type checking on Callable thinks that member variable is a method对 Callable 的 mypy 类型检查认为成员变量是一种方法
【发布时间】:2019-01-19 12:23:50
【问题描述】:

当我通过以下代码运行 mypy 时,我看到了几个错误:

from typing import Callable, Type


def class_creator(outside_reference: Callable[[str], None]) -> Type[object]:
    class SomeClass():
        reference: Callable[[str], None]

        def __init__(self) -> None:
            self.reference = outside_reference
            super().__init__()

        def __str__(self):
            self.reference("SomeClass instance")

    return SomeClass


def callback(string: str) -> None:
    print("Prepping: " + string)


instance = class_creator(callback)()
print(instance)

以下是错误:

test.py:9: error: Cannot assign to a method
test.py:9: error: Invalid self argument "SomeClass" to attribute function "reference" with type "Callable[[str], None]"
test.py:9: error: Incompatible types in assignment (expression has type "Callable[[str], None]", variable has type "Callable[[], None]")

第 9 行是self.reference = outside_reference

我基本上可以肯定我只是误解了某些东西,但我就是看不出哪里出错了。

这是最小的可重复参考。如果我将类型从Callable[[str], None] 更改为int(实际上并没有调用它),那么它运行得很好,不会显示任何错误。只有当我切换到Callable 时,它才会开始显示这些错误。

我的注释应该在这里?

【问题讨论】:

  • 我有一个解决方案:如果您删除 reference 声明(第 6 行),错误将消失。我有一些想法(关于mypy 将可调用字段声明作为方法声明处理),但不确定它们是否正确
  • 这可以解决,当然,但我想要那里的注释,以便使用此代码的任何人都清楚这些类型是什么。

标签: python mypy


【解决方案1】:

目前,MyPy 不支持您这样做。在 GitHub 问题 708 中跟踪了对这种模式的支持:https://github.com/python/mypy/issues/708

在大多数情况下,最接近的模式是使用execute 之类的方法定义一个抽象类,然后让调用者对其实现进行子类化,实例化它,并将实例作为参数而不是回调提供。您可以在较旧的 Java 代码库(Java 8 之前)中看到这种方法,作为匿名内部类的常见用例。当然,这很乏味。

或者,您可以简单地要求 mypy 忽略违规。

【讨论】:

    【解决方案2】:

    在解决https://github.com/python/mypy/issues/708 中的问题之前,解决此问题的一种干净方法是将可调用属性设为可选,并将其包装在带有断言的方法中:

    from typing import Any, Callable, Optional
    class SomeClass:
      _reference: Optional[Callable[[], Any]]
    
      def reference(self) -> Any:
        assert self._reference is not None
        return self._reference()
    
      def __init__(self, reference):
        self.reference = reference
    
    c = SomeClass(lambda: 42)
    print(c.reference())
    
    $ mypy test.py
    Success: no issues found in 1 source file
    

    【讨论】:

    • 与@user240438 的答案相比,此答案有更好的解决方法。
    【解决方案3】:

    类似但更短的解决方法是使用 Union 类型注释成员,复制 Callable 类型:

    from typing import Callable, Union
    class SomeClass:
      reference: Union[Callable[[], int], Callable[[], int]]
    
      def __init__(self, reference: Callable[[], int]):
        self.reference = reference
    
    c = SomeClass(lambda: 42)
    print(c.reference())
    

    【讨论】:

    • 也可以使用 Union[Callable[[], int], NoReturn] 来缩短。
    猜你喜欢
    • 2011-01-13
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多