Search code examples
pythonpython-typingmypypython-dataclasses

How to avoid checking for None when setting Optional dataclass args in __post_init__


Consider a dataclass with a mutable default value for an argument. To be able to instantiate an object with a new default value and not a shared mutable object, we can do something like:

@dataclass
class ClassWithState:
    name: str
    items: Optional[List[str]] = None

    def __post_init__(self) -> None:
        if self.items is None:
            self.items = []

This works as expected. However, whenever I refer to items in some instance of this class, mypy warns that items may be None. For example:

c = ClassWithState("object name")
c.items.append("item1")

MyPy will complain with something like:

Item "None" of "Optional[List[str]]" has no attribute "append".

I don't want to have to add unnecissary checks every time I refer to items such as

assert c.items is not None

everywhere I refer to items. How can I convince mypy that items will never be None?


Solution

  • I'd use field with the default_factory option set:

    from dataclasses import dataclass, field
    from typing import List
    
    
    @dataclass
    class ClassWithState:
        name: str
        items: List[str] = field(default_factory=list)
    
    >>> ClassWithState("Hello")
    ClassWithState(name='Hello', items=[])