使用TypeVar绑定到协议的Python

发布于 2025-02-01 17:02:02 字数 3239 浏览 2 评论 0原文

我正在尝试将python模块中的类型合并到以下错误中:

类型参数“ nedineins.dict [nelidins.int,edgeight'1]”的“ GraphBase”必须是“ union [typing.mutableset [incelions.int],typing.mutableMapping [hindins.int,so.geweightprotocol]]“

我想支持具有任意边缘权重对象的图形,只要可以订购它们。我的注释尝试是定义edgeweight typeVar绑定到edge> edgeweightprotocol协议类。我知道,EdgeWeight实例将不是EdgeWeight Protocol的子类,但它是一个协议,因此根据其定义,它应该具有___ lt ____方法。

我对仿制药,类型和协议的理解在哪里出错?还是这是一个错误?应该如何支持?

我一直在使用mypy 0.910

import collections.abc
from abc import ABCMeta, abstractmethod
from collections import UserList
from typing import (
    Any,
    Callable,
    Dict,
    Iterable,
    MutableMapping,
    MutableSet,
    Protocol,
    Set,
    Tuple,
    TypeVar,
    Union,
)

Edge = Tuple[int, int]
EdgeWeight = TypeVar("EdgeWeight", bound="EdgeWeightProtocol")
Graph = TypeVar("Graph", bound="GraphBase[Any]")


class EdgeWeightProtocol(Protocol):
    def __lt__(self: EdgeWeight, other: EdgeWeight) -> bool:
        ...


S = TypeVar("S", bound=Union[MutableSet[int], MutableMapping[int, EdgeWeightProtocol]])


class GraphBase(UserList[S], metaclass=ABCMeta):

    _item_type: Callable[..., S]

    def __init__(self, n_or_initlist: Union[Iterable[S], int, None] = None,) -> None:
        """
        Initializer for GraphBase.

        Parameters
        ----------
        n_or_initlist : int or iterable, optional
            If `None` (default) initializes a graph of zero order and zero size
            (i.e. no vertices and no edges). If int, initializes a graph of
            order `n_or_initlist` and zero size. Otherwise initialize the graph
            using the iterable `n_or_initlist`.

        """
        if n_or_initlist is None:
            super().__init__()
        elif isinstance(n_or_initlist, int):
            super().__init__((self._item_type() for _ in range(n_or_initlist)))
        elif isinstance(n_or_initlist, collections.abc.Iterable):
            super().__init__(map(self._item_type, n_or_initlist))
        else:
            raise ValueError(
                f"Unsupported type '{type(n_or_initlist).__name__}', "
                "expected int or iterable"
            )

    @abstractmethod
    def remove_edge(self, edge: Edge) -> None:
        pass

    @abstractmethod
    def get_edge_weight(self, edge: Edge) -> EdgeWeightProtocol:
        pass


class WeightedGraph(GraphBase[Dict[int, EdgeWeight]]):

    _item_type = dict

    def add_edge(self, edge: Edge, weight: EdgeWeight) -> None:
        j, i = edge
        self.data[j][i] = weight

    def remove_edge(self, edge: Edge) -> None:
        j, i = edge
        del self.data[j][i]

    def get_edge_weight(self, edge: Edge) -> EdgeWeight:
        j, i = edge
        return self.data[j][i]


class UnweightedGraph(GraphBase[Set[int]]):

    _item_type = set

    def add_edge(self, edge: Edge) -> None:
        j, i = edge
        self.data[j].add(i)

    def remove_edge(self, edge: Edge) -> None:
        j, i = edge
        self.data[j].remove(i)

    def get_edge_weight(self, _edge: Edge) -> float:
        # Always return an edge weight of 1 for an unweighted graph
        return 1

I'm trying to incorporate type checking in a Python module and I am stuck at the following error:

Type argument "builtins.dict[builtins.int, EdgeWeight`1]" of "GraphBase" must be a subtype of "Union[typing.MutableSet[builtins.int], typing.MutableMapping[builtins.int, so.EdgeWeightProtocol]]"

I want to support graphs with arbitrary edge weights objects, as long as they can be ordered. My attempt to annotate this was to define an EdgeWeight TypeVar bound to a EdgeWeightProtocol Protocol class. I understand that an EdgeWeight instance will not be a subclass of EdgeWeightProtocol, but it's a Protocol so it should have a ___lt___ method according to its definition.

Where is my understanding of Generics, TypeVars, and Protocols going wrong? Or is this a bug? How should this be supported?

I have been using mypy 0.910

import collections.abc
from abc import ABCMeta, abstractmethod
from collections import UserList
from typing import (
    Any,
    Callable,
    Dict,
    Iterable,
    MutableMapping,
    MutableSet,
    Protocol,
    Set,
    Tuple,
    TypeVar,
    Union,
)

Edge = Tuple[int, int]
EdgeWeight = TypeVar("EdgeWeight", bound="EdgeWeightProtocol")
Graph = TypeVar("Graph", bound="GraphBase[Any]")


class EdgeWeightProtocol(Protocol):
    def __lt__(self: EdgeWeight, other: EdgeWeight) -> bool:
        ...


S = TypeVar("S", bound=Union[MutableSet[int], MutableMapping[int, EdgeWeightProtocol]])


class GraphBase(UserList[S], metaclass=ABCMeta):

    _item_type: Callable[..., S]

    def __init__(self, n_or_initlist: Union[Iterable[S], int, None] = None,) -> None:
        """
        Initializer for GraphBase.

        Parameters
        ----------
        n_or_initlist : int or iterable, optional
            If `None` (default) initializes a graph of zero order and zero size
            (i.e. no vertices and no edges). If int, initializes a graph of
            order `n_or_initlist` and zero size. Otherwise initialize the graph
            using the iterable `n_or_initlist`.

        """
        if n_or_initlist is None:
            super().__init__()
        elif isinstance(n_or_initlist, int):
            super().__init__((self._item_type() for _ in range(n_or_initlist)))
        elif isinstance(n_or_initlist, collections.abc.Iterable):
            super().__init__(map(self._item_type, n_or_initlist))
        else:
            raise ValueError(
                f"Unsupported type '{type(n_or_initlist).__name__}', "
                "expected int or iterable"
            )

    @abstractmethod
    def remove_edge(self, edge: Edge) -> None:
        pass

    @abstractmethod
    def get_edge_weight(self, edge: Edge) -> EdgeWeightProtocol:
        pass


class WeightedGraph(GraphBase[Dict[int, EdgeWeight]]):

    _item_type = dict

    def add_edge(self, edge: Edge, weight: EdgeWeight) -> None:
        j, i = edge
        self.data[j][i] = weight

    def remove_edge(self, edge: Edge) -> None:
        j, i = edge
        del self.data[j][i]

    def get_edge_weight(self, edge: Edge) -> EdgeWeight:
        j, i = edge
        return self.data[j][i]


class UnweightedGraph(GraphBase[Set[int]]):

    _item_type = set

    def add_edge(self, edge: Edge) -> None:
        j, i = edge
        self.data[j].add(i)

    def remove_edge(self, edge: Edge) -> None:
        j, i = edge
        self.data[j].remove(i)

    def get_edge_weight(self, _edge: Edge) -> float:
        # Always return an edge weight of 1 for an unweighted graph
        return 1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文