Search code examples
pythonpython-typingpython-dataclasses

Dataclass Optional Field that is Inferred if Missing


I want my dataclass to have a field that can either be provided manually, or if it isn't, it is inferred at initialization from the other fields. MWE:

from collections.abc import Sized
from dataclasses import dataclass
from typing import Optional


@dataclass
class Foo:
    data: Sized
    index: Optional[list[int]] = None

    def __post_init__(self):
        if self.index is None:
            self.index = list(range(len(self.data)))

reveal_type(Foo.index)           # Union[None, list[int]]
reveal_type(Foo([1,2,3]).index)  # Union[None, list[int]]

How can this be implemented in a way such that:

  1. It complies with mypy type checking
  2. index is guaranteed to be of type list[int]

I considered using default_factory(list), however, then how does one distinguish the User passing index=[] from the sentinel value? Is there a proper solution besides doing

index: list[int] = None  # type: ignore[assignment]

Solution

  • Use NotImplemented

    from collections.abc import Sized
    from dataclasses import dataclass
    
    
    @dataclass
    class Foo:
        data: Sized
        index: list[int] = NotImplemented
    
        def __post_init__(self):
            if self.index is NotImplemented:
                self.index = list(range(len(self.data)))