Ice*_*ube 8 python type-hinting python-3.x python-typing
我在以下代码中遇到类型检查器错误,我很想了解如何解决该错误。
下面的基类有一个抽象类方法,我希望从它继承的每个子类都将实现一个decode返回子类实例的函数。
from abc import ABC, abstractmethod
from typing import TypeVar
TMetricBase = TypeVar("TMetricBase", bound="MetricBase")
class MetricBase(ABC):
@abstractmethod
def add(self, element: str) -> None:
pass # pragma: no cover
@classmethod
@abstractmethod
def decode(cls, json_str: str) -> TMetricBase:
pass # pragma: no cover
Run Code Online (Sandbox Code Playgroud)
子类如下所示
import json
from typing import Any, Callable, List, Mapping, Optional
from something import MetricBase, TMetricBase
class DiscreteHistogramMetric(MetricBase):
def __init__(self, histogram: Optional[Mapping[str, int]]) -> None:
super().__init__()
self._histogram = dict(histogram) if histogram else {}
def add(self, element: str) -> None:
self._histogram[element] = self._histogram.get(element, 0) + 1
@classmethod
def decode(cls, json_str: str) -> "DiscreteHistogramMetric":
json_obj = json.loads(json_str)
histogram_map = json_obj["DiscreteHistogramMetric"]
return cls(histogram=histogram_map)
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
error: Return type of "decode" incompatible with supertype "MetricBase"
Run Code Online (Sandbox Code Playgroud)
将decode的返回类型更改为 时TMetricBase,出现以下错误:
error: Incompatible return value type (got "DiscreteHistogramMetric", expected "TMetricBase")
Run Code Online (Sandbox Code Playgroud)
该错误与您如何在decode. 目前还不清楚这究竟意味着什么——您或多或少地试图声明 的每个子类都MetricBase需要支持返回 的任何其他任意子类MetricBase,它会以某种方式根据该函数的调用方式神奇地推断出。
这在 Python 中是不可能做到的。
您需要做的是以下操作之一:
MetricBase一个泛型类并让您的子类继承MetricBase.TMetricBase在decode参数中使用。(这样,我们实际上可以推断出返回类型应该是什么)。我假设您已经考虑了第一个解决方案并拒绝了它:它会使我们的程序类型检查,但也会使该decode方法有些无用/需要一些笨拙的转换。
第二个解决方案看起来像这样:
from abc import ABC, abstractmethod
from typing import TypeVar, Generic
TMetricBase = TypeVar("TMetricBase", bound="MetricBase")
class MetricBase(ABC, Generic[TMetricBase]):
@classmethod
@abstractmethod
def decode(cls, json_str: str) -> TMetricBase:
pass
class DiscreteHistogramMetric(MetricBase['DiscreteHistogramMetric']):
@classmethod
def decode(cls, json_str: str) -> "DiscreteHistogramMetric":
pass
Run Code Online (Sandbox Code Playgroud)
通过使用DiscreteHistogramMetric子类MetricBase[DiscreteHistogramMetric]而不是MetricBase直接使用,我们实际上可以将类型变量限制为有意义的东西。
不过,这个解决方案仍然有点笨拙——必须子类化MetricBase要求我们在使用 MetricBase 的地方开始使用泛型,这很烦人。
表面上的第三个解决方案最初听起来更笨拙:我们是要添加一些额外的虚拟第三个参数还是一些废话?但事实证明,我们可以使用一个很好的技巧——我们可以使用通用 self来注释cls变量!
通常,该变量的类型是推断出来的,不需要注释,但在这种情况下,这样做很有帮助:我们可以使用有关究竟cls是什么的信息来帮助生成更精确的返回类型。
这是它的样子:
from abc import ABC, abstractmethod
from typing import TypeVar, Type
TMetricBase = TypeVar("TMetricBase", bound="MetricBase")
class MetricBase(ABC):
@classmethod
@abstractmethod
def decode(cls: Type[TMetricBase], json_str: str) -> TMetricBase:
pass
class DiscreteHistogramMetric(MetricBase):
def __init__(self, something: str) -> None:
pass
@classmethod
def decode(cls: Type[TMetricBase], json_str: str) -> TMetricBase:
# Note that we need to use create the class by using `cls` instead of
# using `DiscreteHistogramMetric` directly.
return cls("blah")
Run Code Online (Sandbox Code Playgroud)
有点不幸的是,我们需要继续在子类中使用 TypeVars 而不是像您在问题中所做的那样更简单地定义它——我相信这种行为是mypy 中的一个错误。
但是,它确实可以解决问题:执行DiscreteHistogramMetric.decode("blah")将按TMetricBase预期返回 a 。
与第一种方法不同的是,混乱至少很好地局限于decode方法,并且不需要您在使用MetricBase类的任何地方开始使用泛型。
PEP 673引入了“ SelfType”,它应该在 Python 3.11 中可用,并且可以通过typing-extensions包向后移植。
from abc import ABC, abstractmethod
from typing import Self
class MetricBase(ABC):
@classmethod
@abstractmethod
def decode(cls, json_str: str) -> Self:
pass
class DiscreteHistogramMetric(MetricBase):
def __init__(self, something: str) -> None:
pass
@classmethod
def decode(cls, json_str: str) -> Self:
return cls("blah")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4299 次 |
| 最近记录: |