Type hint issue when base class is parameterized on a value type and has methods that return that type

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

Type hint issue when base class is parameterized on a value type and has methods that return that type

问题

这是一个用于说明问题的玩具示例:

  1. from typing import Type
  2. from dataclasses import dataclass, asdict
  3. import json
  4. @dataclass
  5. class ValueType1:
  6. x: int
  7. y: int
  8. @dataclass
  9. class ValueType2:
  10. x: int
  11. z: str
  12. class FooBase:
  13. def __init__(self, value_cls: Type[ValueType1|ValueType2], name: str):
  14. self.name = name
  15. self.value_cls = value_cls
  16. def save(self, value: ValueType1|ValueType2):
  17. with open(self.name+'.json', 'w') as f:
  18. json.dump(asdict(value), f)
  19. def load(self) -> ValueType1|ValueType2:
  20. with open(self.name+'.json', 'r') as f:
  21. return self.value_cls(**json.load(f))
  22. class Foo1(FooBase):
  23. def __init__(self):
  24. super().__init__(value_cls=ValueType1, name='1')
  25. class Foo2(FooBase):
  26. def __init__(self):
  27. super().__init__(value_cls=ValueType2, name='2')
  28. foo1 = Foo1()
  29. foo2 = Foo2()
  30. foo1.save(ValueType1(x=10, y=20))
  31. foo2.save(ValueType2(x=10, z='a'))
  32. res1 = foo1.load()
  33. res2 = foo2.load()
  34. print(res1.y)
  35. print(res2.z)

这个示例可以正常工作,如预期地打印出20和'a',但是在res1.yres2.z处会显示类型错误(仅显示res1.y错误)。

  1. Type of "y" is unknownPylancereportUnknownMemberType
  2. Type of "y" is partially unknown
  3. Type of "y" is "int | Unknown"PylancereportUnknownMemberType
  4. Argument type is partially unknown
  5. Argument corresponds to parameter "values" in function "print"
  6. Argument type is "int | Unknown"PylancereportUnknownArgumentType
  7. Cannot access member "y" for type "ValueType2"
  8. Member "y" is unknownPylancereportGeneralTypeIssues
  9. (variable) y: int | Unknown

我想知道是否有一些类型提示的技巧可以解决这个问题,让res1的类型清晰地显示为ValueType1。谢谢!

英文:

This a toy example to illustrate the issue:

  1. from typing import Type
  2. from dataclasses import dataclass, asdict
  3. import json
  4. @dataclass
  5. class ValueType1:
  6. x: int
  7. y: int
  8. @dataclass
  9. class ValueType2:
  10. x: int
  11. z: str
  12. class FooBase:
  13. def __init__(self, value_cls: Type[ValueType1|ValueType2], name: str):
  14. self.name = name
  15. self.value_cls = value_cls
  16. def save(self, value: ValueType1|ValueType2):
  17. with open(self.name+'.json', 'w') as f:
  18. json.dump(asdict(value), f)
  19. def load(self) -> ValueType1|ValueType2:
  20. with open(self.name+'.json', 'r') as f:
  21. return self.value_cls(**json.load(f))
  22. class Foo1(FooBase):
  23. def __init__(self):
  24. super().__init__(value_cls=ValueType1, name='1')
  25. class Foo2(FooBase):
  26. def __init__(self):
  27. super().__init__(value_cls=ValueType2, name='2')
  28. foo1 = Foo1()
  29. foo2 = Foo2()
  30. foo1.save(ValueType1(x=10, y=20))
  31. foo2.save(ValueType2(x=10, z='a'))
  32. res1 = foo1.load()
  33. res2 = foo2.load()
  34. print(res1.y)
  35. print(res2.z)

This works fine and prints 20 and 'a' as expected but typing errors are shown at res1.y and res2.z of this sort (only res1.y errors shown):

  1. Type of "y" is unknownPylancereportUnknownMemberType
  2. Type of "y" is partially unknown
  3. Type of "y" is "int | Unknown"PylancereportUnknownMemberType
  4. Argument type is partially unknown
  5. Argument corresponds to parameter "values" in function "print"
  6. Argument type is "int | Unknown"PylancereportUnknownArgumentType
  7. Cannot access member "y" for type "ValueType2"
  8. Member "y" is unknownPylancereportGeneralTypeIssues
  9. (variable) y: int | Unknown

I'm wondering if there is some type hinting magic that can resolve this and make it clear that res1 is of type ValueType1. Thanks!

答案1

得分: 1

你可以这样做:

  1. from typing import Callable
  2. class Foo1(FooBase):
  3. load: Callable[[], ValueType1]
  4. def __init__(self):
  5. super().__init__(value_cls=ValueType1, name='1')
  6. class Foo2(FooBase):
  7. load: Callable[[], ValueType2]
  8. def __init__(self):
  9. super().__init__(value_cls=ValueType2, name='2')

有些编辑器可能会将 load() 识别为属性而不是函数,并且颜色显示可能不正确,但类型提示将正常工作。

英文:

you could do this:

  1. from typing import Callable
  2. class Foo1(FooBase):
  3. load: Callable[[], ValueType1]
  4. def __init__(self):
  5. super().__init__(value_cls=ValueType1, name='1')
  6. class Foo2(FooBase):
  7. load: Callable[[], ValueType2]
  8. def __init__(self):
  9. super().__init__(value_cls=ValueType2, name='2')

Some editors will now pick load() up as a attribute and not a function and color it wrong though. But type hints will work properly

huangapple
  • 本文由 发表于 2023年7月3日 02:06:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76600197.html
匿名

发表评论

匿名网友

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

确定