Nevermore
Nevermore

Reputation: 358

MyPy fails dataclass argument with optional list of objects type

I'm having trouble getting MyPy to pass my script which contains a dataclass Bar with an optional argument foos that holds a list of Foo objects and defaults to an empty list.

Stranger still, adding a method Bar.sum_foos() which iterates through self.foos raises a second MyPy error. Am I misunderstanding how to set the correct types, or is something else going on?

script.py

from __future__ import annotations
from typing import Optional
from dataclasses import dataclass, field


class Foo():
    pass


@dataclass
class Bar():
    foos: Optional[list[Foo]] = field(default_factory = list)

    def sum_foos(self) -> float:
        for foo in self.foos:
            pass
        return 0.0

2 MyPy errors

$ mypy script.py
script.py:12: error: Incompatible types in assignment (expression has type "List[_T]", variable has type "Optional[List[Foo]]")
script.py:15: error: Item "None" of "Optional[List[Foo]]" has no attribute "__iter__" (not iterable)
Found 2 errors in 1 file (checked 1 source file)

Versions

$ python --version
Python 3.8.12
$ mypy --version
mypy 0.950 (compiled: yes)

Upvotes: 0

Views: 1210

Answers (1)

STerliakov
STerliakov

Reputation: 7943

Well, the code in your question is almost fine as-is. Optional[X] is equivalent to X | None - why do you make your list optional? This field is never None, factory means that foos attribute will be an empty list, if not passed to the constructor. Thus declaring as list[Foo] is more correct and eliminates all errors.

from __future__ import annotations
from typing import Optional
from dataclasses import dataclass, field


class Foo():
    pass


@dataclass
class Bar():
    foos: list[Foo] = field(default_factory=list)

    def sum_foos(self) -> float:
        for foo in self.foos:
            pass
        return 0.0

Upvotes: 2

Related Questions