I have a data class having as member a list of objects of another class having as member an object of type Optional[CustomType]
, and I need to call a method of CustomType
class on that member, only when the member is not None
.
Even if such check is explicitly performed, mypy still complains about the missing attribute of the item None
of the CustomType | None
union.
Here follows a working example using str
as CustomType
that highlights the issue:
from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True)
class Sample:
tag: Optional[str] = None
@dataclass(frozen=True)
class Container:
samples: list[Sample]
if __name__ == "__main__":
containers: list[Container] = []
tags: list[str] = []
for container in containers:
for index in range(len(container.samples)):
if container.samples[index].tag is not None:
tags.append(container.samples[index].tag.capitalize())
This gives Item "None" of "str | None" has no attribute "capitalize" Mypyunion-attr
error on mypy.
I’ve discovered that the issue vanishes if one avoids indexing and iterates over the elements directly (which I may not)
for container in containers:
for sample in container.samples:
if sample.tag is not None:
tags.append(sample.tag.capitalize())
so it seems that mypy struggles resolving the type because of the nested structure.
Other workarounds which I cannot use involve asserting or assigning the value to a temporary variable for checking. The walrus operator assignment would do as well:
for container in containers:
for index in range(len(container.samples)):
if (tag := container.samples[index].tag) is not None:
tags.append(tag.capitalize())
but apparently cannot be used in a more complex context (Operator ":=" is not allowed in this context without surrounding parentheses
).
What am I missing here?
Vexx23 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
2