How should we type hint for dataclasses which can accept multiple input types but cast them all to a particular type during, e.g. __post_init__
. Example:
from dataclasses import dataclass
from typing import Collection, List
@dataclass
class Foo1:
input_list: Collection
def __post_init__(self):
self.input_list = list(self.input_list)
@dataclass
class Foo2:
input_list: List
def __post_init__(self):
self.input_list = list(self.input_list)
For class Foo1
we will get type warnings if we try something like foo1.input_list.append(0)
because the type checker doesn't know that foo1.input_list
is a List
(it only knows it is a Collection
). On the other hand, class Foo2
will give type warning for foo2 = Foo2((1, 2))
because it expects a List
input, not a Tuple
.
What is the appropriate way to write a dataclass (or any class) that does mild type conversion on its attributes during __post_init__
?
Sadly, this does not appear to be a case where dataclasses
will do the heavy-lifting for you. If you want the annotations to differ between the __init__
arguments and the attributes themselves, at present, you need to write the __init__
by hand. For a class like this one, it's pretty trivial:
from collections.abc import Collection
@dataclass
class Foo:
input_list: list[int]
def __init__(self, input_list: Collection[int]) -> None:
self.input_list = list(input_list)
foo = Foo((1, 2))
foo.input_list.append(0) # No objections raised by mypy, even in --strict mode
Obviously not ideal if the dataclass
is more complicated, but it does work (mypy
raises no complaints if you do foo = Foo((1, 2))
, foo.input_list.append(0)
).