Unbound TypeVar variable in overloaded class

huangapple go评论94阅读模式
英文:

Unbound TypeVar variable in overloaded class

问题

我有以下的代码,这是一个简化版本的Python实体组件系统:

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar, overload

if TYPE_CHECKING:
    from collections.abc import Generator

T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")

class Registry:
    def __init__(self) -> None:
        self._next_game_object_id = 0
        self._components: dict[type[T], set[int]] = {}
        self._game_objects: dict[int, dict[type[T], T]] = {}

    @overload
    def get_components(
        self,
        component: type[T],
    ) -> Generator[tuple[int, T], None, None]:
        ...

    @overload
    def get_components(
        self,
        component: type[T],
        component_two: type[T1],
    ) -> Generator[tuple[int, T, T1], None, None]:
        ...

    @overload
    def get_components(
        self,
        component: type[T],
        component_two: type[T1],
        component_three: type[T2],
    ) -> Generator[tuple[int, T, T1, T2], None, None]:
        ...

    def get_components(self, *components: type[T]) -> Generator[tuple[int, tuple[T, ...]], None, None]:
        game_object_ids = set.intersection(
            *(self._components[component] for component in components),
        )

        for game_object_id in game_object_ids:
            yield game_object_id, tuple(
                self._game_objects[game_object_id][component]
                for component in components
            )

然而,我遇到了一些mypy错误,我无法弄清楚如何解决。其中一个错误说TypeVar T 未绑定,另一个说get_components 不接受所有可能的签名 1、2 和 3 的参数。我该如何修复这些错误?

英文:

I have this following code which is a simplified version of an entity component system in python:

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar, overload

if TYPE_CHECKING:
    from collections.abc import Generator


T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")


class Registry:
    def __init__(self) -> None:
        self._next_game_object_id = 0
        self._components: dict[type[T], set[int]] = {}
        self._game_objects: dict[int, dict[type[T], T]] = {}

    @overload
    def get_components(
        self,
        component: type[T],
    ) -> Generator[tuple[int, T], None, None]:
        ...

    @overload
    def get_components(
        self,
        component: type[T],
        component_two: type[T1],
    ) -> Generator[tuple[int, T, T1], None, None]:
        ...

    @overload
    def get_components(
        self,
        component: type[T],
        component_two: type[T1],
        component_three: type[T2],
    ) -> Generator[tuple[int, T, T1, T2], None, None]:
        ...

    def get_components(self, *components: type[T]) -> Generator[tuple[int, tuple[T, ...]], None, None]:
        game_object_ids = set.intersection(
            *(self._components[component] for component in components),
        )

        for game_object_id in game_object_ids:
            yield game_object_id, tuple(
                self._game_objects[game_object_id][component]
                for component in components
            )

However, I'm getting some mypy errors which I cannot figure out. One of which says that the TypeVar T is unbound and another says that get_components does not accept all possible arguments of signature 1, 2, and 3. How can I fix these errors?

答案1

得分: 1

以下是翻译好的部分:

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar, overload, Generic

if TYPE_CHECKING:
    from collections.abc import Generator

T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")

class Registry(Generic[T, T1, T2]):
    def __init__(self) -> None:
        self._next_game_object_id = 0
        self._components: dict[type[T | T1 | T2], set[int]] = {}
        self._game_objects: dict[int, dict[type[T | T1 | T2], T | T1 | T2]] = {}

    @overload
    def get_components(
        self,
        __component: type[T],
    ) -> Generator[tuple[int, tuple[T]], None, None]:
        ...

    @overload
    def get_components(
        self,
        __component: type[T],
        __component_two: type[T1],
    ) -> Generator[tuple[int, tuple[T, T1]], None, None]:
        ...

    @overload
    def get_components(
        self,
        __component: type[T],
        __component_two: type[T1],
        __component_three: type[T2],
    ) -> Generator[tuple[int, tuple[T, T1, T2]], None, None]:
        ...

    def get_components(
        self,
        *components: type[T | T1 |T2]
    ) -> Generator[tuple[int, tuple[T | T1 | T2, ...]], None, None]:
        game_object_ids = set.intersection(
            *(self._components[component] for component in components),
        )

        for game_object_id in game_object_ids:
            yield game_object_id, tuple(
                self._game_objects[game_object_id][component]
                for component in components
            )

注意:在代码中我替换了HTML实体 " 为普通的双引号 " 以保持代码的正确性。

英文:

Possible solution:

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar, overload, Generic

if TYPE_CHECKING:
    from collections.abc import Generator


T = TypeVar("T")
T1 = TypeVar("T1")
T2 = TypeVar("T2")


class Registry(Generic[T, T1, T2]):
    def __init__(self) -> None:
        self._next_game_object_id = 0
        self._components: dict[type[T | T1 | T2], set[int]] = {}
        self._game_objects: dict[int, dict[type[T | T1 | T2], T | T1 | T2]] = {}

    @overload
    def get_components(
        self,
        __component: type[T],
    ) -> Generator[tuple[int, tuple[T]], None, None]:
        ...

    @overload
    def get_components(
        self,
        __component: type[T],
        __component_two: type[T1],
    ) -> Generator[tuple[int, tuple[T, T1]], None, None]:
        ...

    @overload
    def get_components(
        self,
        __component: type[T],
        __component_two: type[T1],
        __component_three: type[T2],
    ) -> Generator[tuple[int, tuple[T, T1, T2]], None, None]:
        ...

    def get_components(
        self,
        *components: type[T | T1 |T2]
    ) -> Generator[tuple[int, tuple[T | T1 | T2, ...]], None, None]:
        game_object_ids = set.intersection(
            *(self._components[component] for component in components),
        )

        for game_object_id in game_object_ids:
            yield game_object_id, tuple(
                self._game_objects[game_object_id][component]
                for component in components
            )

Explanations:

  1. Inheriting from typing.Generic binds the TypeVars to the class through making it a generic class which depends on those TypeVars (More on Generics)
  2. Generalizing the method definition of get_components is crucial to accept all the different types that can occur through the overloads
  3. wrapping all the T, T, T1 and T, T1, T2 inside a tuple[...] as this ensures consistency with the return annotation of the now generalized method definition
  4. Making the components in the @overloaded methods positional only. This behaviour of *args and @overload seems not very well documented, only through a resolved issue on GitHub

huangapple
  • 本文由 发表于 2023年7月31日 19:36:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76803230.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定