Search code examples
pythonpython-3.xtypescastingpython-dataclasses

Python dataclass type hints and input type conversions


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__?


Solution

  • 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)).