子类装饰方法的类型不兼容 - python
Posted
技术标签:
【中文标题】子类装饰方法的类型不兼容 - python【英文标题】:Type incompatibility for subclass' decorated method - python 【发布时间】: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 路径不会失去功能。
【参考方案1】:
我认为您的意思是 dec 将返回 Callable 而不是 T。 应该是:
def dec(f: Callable[..., T]) -> Callable[..., T]:
def p(self):
return getattr(self, '_' + f.__name__)
return cast(Callable[..., T], property(p))
【讨论】:
我不是 100% 清楚这一点,但我不认为这是Callable[..., T]
。它应该是一个property
,但由于在任何被称为 this 的语法上都具有与 T 相同的类型,所以我们可以安全地转换为 T以上是关于子类装饰方法的类型不兼容 - python的主要内容,如果未能解决你的问题,请参考以下文章
使用 UItableViewCell 类型的表达式初始化“CustomCellView *”的不兼容指针类型