【发布时间】:2019-03-15 15:07:24
【问题描述】:
我遇到了一个错误
$ mypy python.py
python.py:34: error: Signature of "fn" incompatible with supertype "B"
python.py 在哪里
from typing import Callable, TypeVar, cast
T = TypeVar('T')
def dec(f: Callable[..., T]) -> T:
def p(self):
return getattr(self, '_' + f.__name__)
return cast(T, property(p))
class X:
pass
class Y(X):
pass
class A:
_fn = Y()
class B(A):
@dec
def fn(self) -> X:
return X()
class C(B):
@dec
def fn(self) -> Y:
return Y()
这里,装饰器dec 意味着做两件事
- 将对应的方法提升为属性
- 将调用重定向到名称带有附加下划线的属性
我不明白为什么 mypy 无法确定 Y 继承自 X。如果我将 -> Y 替换为 -> X,或者删除装饰器,则不会出现错误。
我已经用 mypy 0.630 和 Python 3.5.2、3.6.6 和 3.7.0 尝试过这个
编辑 正如 cmets 中所指出的,最初发布的 A._fn 的类型与 C.fn 不兼容。我已编辑 A._fn 以消除此问题。错误没有改变。
编辑一些上下文 - A 是一个配置模式类,看起来更像
class A:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, '_' + k, v)
我们希望用户像c = C(fn=Y()); c.fn # gives a Y 一样使用它。最初该问题将A_.fn 作为返回Y() 的方法。更正这一点并不会影响错误
编辑我已将此作为一个 mypy 错误 https://github.com/python/mypy/issues/5836
【问题讨论】:
-
你能显示完整的错误信息吗?
-
我在运行这个文件时没有收到任何错误...你能显示你用来运行文件的完整命令吗?你用的是什么版本的 python & mypy?
-
Y不是X。 -
那么这里是什么意思呢?您将一个承诺返回
Y的函数替换为一个返回X的对象。您限制了返回值。任何调用C().fn()的人都可以合理地期望能够使用Y()方法和属性,但X()不会拥有这些。这违反了您声明fn()的合同。我通常会在这里指向covariance and contravariance,但你不能精确地在返回值上使用逆变,因为你不能仅仅删除这样的功能。 -
换一种说法,当装饰器承诺返回完全相同的类型时,为什么 Mypy 允许通过装饰器从
Y转换到X?dec接受一个返回T的Callable,并生成T作为结果。Y->X与该签名不匹配,并且从Y到X的 sane 路径不会失去功能。
标签: python properties python-decorators mypy type-hinting