对Callable的mypy类型检查认为成员变量是一种方法

时间:2018-08-12 17:06:26

标签: python mypy

当我在以下代码上运行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时,它才会开始显示这些错误。

我的注释应该在这里什么?

3 个答案:

答案 0 :(得分:2)

https://github.com/python/mypy/issues/708中的问题得到解决之前,一种解决此问题的干净方法是使callable属性成为可选属性,并将其包装在带有assert的方法中:

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

答案 1 :(得分:0)

暂时,MyPy不支持您这样做。在GitHub第708期中跟踪了对这种模式的支持:https://github.com/python/mypy/issues/708

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

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

答案 2 :(得分:0)

一种类似但更短的解决方法是使用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())