4 ms·
One caveat of the tip in the "Deduplicating shared variant state" section about including an underspecified discriminator field in the base class, is that it do
by reubenmorais 2y ago
One caveat of the tip in the "Deduplicating shared variant state" section about including an underspecified discriminator field in the base class, is that it doesn't play well if you're using Literals instead of Enums as the discriminator type. Python does not allow you to narrow a literal type of a field in a subclass, so the following doesn't type check:
from typing import Literal
class _FrobulatedBase:
kind: Literal['foo', 'bar']
value: str
class Foo(_FrobulatedBase):
kind: Literal['foo'] = 'foo'
foo_specific: int
class Bar(_FrobulatedBase):
kind: Literal['bar'] = 'bar'
bar_specific: bool
"kind" overrides symbol of same name in class "_FrobulatedBase"
Variable is mutable so its type is invariant
Override type "Literal['foo']" is not the same as base type "Literal['foo', 'bar']"
https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMoAySMApiAIYA2AUNQMaXkDOTUA%2BgGLgBGAro6QAmAIWYkAXNSjSoAa1SDxhYmSoBtAOTAwYDQBooG7uRAaAulJkA3KrwlQmMELXqMWUDjoAUnHv3JCokwkAJSSMnIKSkSkFJSa2rpmUAC8hokaltKJbEwIJHRIwEh0SqgwLgzMrKIgPlxgfAIkImJhWZEoisqx6kYm5qmGxqYdI7n5hcWlUNw6NEA https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMo...
- yorwba 2y ago> it doesn't play well if you're using Literals instead of Enums as the discriminator type The original example code with Enums doesn't type-check either, and for the same reason: If the type checker allowed that, someone could take an object of type Foo, assign it to a variable of type _FrobulatedBase, then use that variable to modify the kind field to 'bar' and now you have an illegal Foo with kind 'bar'.
- woodruffw 2y agomypy typechecks that just fine[1]. However, I think that's possibly a bug :-) -- I agree that narrowing a literal via subclassing is unsound. That's why the example in the blog used `str` for the superclass, not the closure of all `Literal` variants. (I use this pattern pretty extensively in Python codebases that are typechecked with mypy, and I haven't run into many issues with mypy failing to understand the variant shapes -- the exception to this so far has been with `RootModel`, where mypy has needed Pydantic's mypy plugin[2] to understand the relationship between the "root" type and its underlying union. But it's possible that this is essentially unsound as well.) [1]: https://mypy-play.net/?mypy=latest&python=3.12&gist=f35da62ec4ac59f06360a758ca5f58f6 https://mypy-play.net/?mypy=latest&python=3.12&gist=f35da62e... [2]: https://docs.pydantic.dev/latest/integrations/mypy/ https://docs.pydantic.dev/latest/integrations/mypy/
- reubenmorais 2y agoUsing str in the superclass equally unsound and also doesn't type check. There's no good way to do it, as the discriminator type is by definition disjoint between all kinds.
- deleted 2y ago[deleted]
- sevensor 2y agoThe problem here seems to be inheritance. Why share a base class? Why not write Frobulated = Foo|Bar and go about your day writing functions like def frotz(x: Frobulated) -> str: return f”{x.value} is the value of x”