Unbound TypeVar variable in overloaded class

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

Unbound TypeVar variable in overloaded class

问题

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

  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING, TypeVar, overload
  3. if TYPE_CHECKING:
  4. from collections.abc import Generator
  5. T = TypeVar("T")
  6. T1 = TypeVar("T1")
  7. T2 = TypeVar("T2")
  8. class Registry:
  9. def __init__(self) -> None:
  10. self._next_game_object_id = 0
  11. self._components: dict[type[T], set[int]] = {}
  12. self._game_objects: dict[int, dict[type[T], T]] = {}
  13. @overload
  14. def get_components(
  15. self,
  16. component: type[T],
  17. ) -> Generator[tuple[int, T], None, None]:
  18. ...
  19. @overload
  20. def get_components(
  21. self,
  22. component: type[T],
  23. component_two: type[T1],
  24. ) -> Generator[tuple[int, T, T1], None, None]:
  25. ...
  26. @overload
  27. def get_components(
  28. self,
  29. component: type[T],
  30. component_two: type[T1],
  31. component_three: type[T2],
  32. ) -> Generator[tuple[int, T, T1, T2], None, None]:
  33. ...
  34. def get_components(self, *components: type[T]) -> Generator[tuple[int, tuple[T, ...]], None, None]:
  35. game_object_ids = set.intersection(
  36. *(self._components[component] for component in components),
  37. )
  38. for game_object_id in game_object_ids:
  39. yield game_object_id, tuple(
  40. self._game_objects[game_object_id][component]
  41. for component in components
  42. )

然而,我遇到了一些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:

  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING, TypeVar, overload
  3. if TYPE_CHECKING:
  4. from collections.abc import Generator
  5. T = TypeVar("T")
  6. T1 = TypeVar("T1")
  7. T2 = TypeVar("T2")
  8. class Registry:
  9. def __init__(self) -> None:
  10. self._next_game_object_id = 0
  11. self._components: dict[type[T], set[int]] = {}
  12. self._game_objects: dict[int, dict[type[T], T]] = {}
  13. @overload
  14. def get_components(
  15. self,
  16. component: type[T],
  17. ) -> Generator[tuple[int, T], None, None]:
  18. ...
  19. @overload
  20. def get_components(
  21. self,
  22. component: type[T],
  23. component_two: type[T1],
  24. ) -> Generator[tuple[int, T, T1], None, None]:
  25. ...
  26. @overload
  27. def get_components(
  28. self,
  29. component: type[T],
  30. component_two: type[T1],
  31. component_three: type[T2],
  32. ) -> Generator[tuple[int, T, T1, T2], None, None]:
  33. ...
  34. def get_components(self, *components: type[T]) -> Generator[tuple[int, tuple[T, ...]], None, None]:
  35. game_object_ids = set.intersection(
  36. *(self._components[component] for component in components),
  37. )
  38. for game_object_id in game_object_ids:
  39. yield game_object_id, tuple(
  40. self._game_objects[game_object_id][component]
  41. for component in components
  42. )

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

以下是翻译好的部分:

  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING, TypeVar, overload, Generic
  3. if TYPE_CHECKING:
  4. from collections.abc import Generator
  5. T = TypeVar("T")
  6. T1 = TypeVar("T1")
  7. T2 = TypeVar("T2")
  8. class Registry(Generic[T, T1, T2]):
  9. def __init__(self) -> None:
  10. self._next_game_object_id = 0
  11. self._components: dict[type[T | T1 | T2], set[int]] = {}
  12. self._game_objects: dict[int, dict[type[T | T1 | T2], T | T1 | T2]] = {}
  13. @overload
  14. def get_components(
  15. self,
  16. __component: type[T],
  17. ) -> Generator[tuple[int, tuple[T]], None, None]:
  18. ...
  19. @overload
  20. def get_components(
  21. self,
  22. __component: type[T],
  23. __component_two: type[T1],
  24. ) -> Generator[tuple[int, tuple[T, T1]], None, None]:
  25. ...
  26. @overload
  27. def get_components(
  28. self,
  29. __component: type[T],
  30. __component_two: type[T1],
  31. __component_three: type[T2],
  32. ) -> Generator[tuple[int, tuple[T, T1, T2]], None, None]:
  33. ...
  34. def get_components(
  35. self,
  36. *components: type[T | T1 |T2]
  37. ) -> Generator[tuple[int, tuple[T | T1 | T2, ...]], None, None]:
  38. game_object_ids = set.intersection(
  39. *(self._components[component] for component in components),
  40. )
  41. for game_object_id in game_object_ids:
  42. yield game_object_id, tuple(
  43. self._game_objects[game_object_id][component]
  44. for component in components
  45. )

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

英文:

Possible solution:

  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING, TypeVar, overload, Generic
  3. if TYPE_CHECKING:
  4. from collections.abc import Generator
  5. T = TypeVar("T")
  6. T1 = TypeVar("T1")
  7. T2 = TypeVar("T2")
  8. class Registry(Generic[T, T1, T2]):
  9. def __init__(self) -> None:
  10. self._next_game_object_id = 0
  11. self._components: dict[type[T | T1 | T2], set[int]] = {}
  12. self._game_objects: dict[int, dict[type[T | T1 | T2], T | T1 | T2]] = {}
  13. @overload
  14. def get_components(
  15. self,
  16. __component: type[T],
  17. ) -> Generator[tuple[int, tuple[T]], None, None]:
  18. ...
  19. @overload
  20. def get_components(
  21. self,
  22. __component: type[T],
  23. __component_two: type[T1],
  24. ) -> Generator[tuple[int, tuple[T, T1]], None, None]:
  25. ...
  26. @overload
  27. def get_components(
  28. self,
  29. __component: type[T],
  30. __component_two: type[T1],
  31. __component_three: type[T2],
  32. ) -> Generator[tuple[int, tuple[T, T1, T2]], None, None]:
  33. ...
  34. def get_components(
  35. self,
  36. *components: type[T | T1 |T2]
  37. ) -> Generator[tuple[int, tuple[T | T1 | T2, ...]], None, None]:
  38. game_object_ids = set.intersection(
  39. *(self._components[component] for component in components),
  40. )
  41. for game_object_id in game_object_ids:
  42. yield game_object_id, tuple(
  43. self._game_objects[game_object_id][component]
  44. for component in components
  45. )

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:

确定