I am starting experiment with type annotations in Python 3 and have a problem with a function exclude_filter, specifically annotating the items in following code snippet (I am posting this unannotated). Briefly speaking I am trying to iterate over a list and filter out some items based on some criteria. And the type of the item in list is either instance of class or tuple of those instances in which case I am looking for criteria only in first member of the tuple.
@dataclass
class BaseItem:
name: str
something: int
def exclude_filter(items, matches):
def item_matched(item, matches):
name = item[0].name if isinstance(item, tuple) else item.name
for match in matches:
if match in name:
return True
return False
items[:] = [i for i in items if not item_matched(i, matches)]
FOOS = [BaseItem("1st foo", 10), BaseItem("2nd foo", 11)]
BARS = [BaseItem("1st bar", 20), BaseItem("2nd bar", 22)]
FOOS_AND_BARS = list(zip(FOOS, BARS))
exclude_filter(FOOS, ["1st"])
exclude_filter(BARS, ["2nd"])
exclude_filter(FOOS_AND_BARS, ["1st"])
print(FOOS)
# [BaseItem(name='2nd foo', something=11)]
print(BARS)
# [BaseItem(name='1st bar', something=20)]
print(FOOS_AND_BARS)
# [(BaseItem(name='2nd foo', something=11), BaseItem(name='2nd bar', something=22))]
I've tried obviously wrong items: List[BaseItem] with result:
Argument 1 to "exclude_filter" has incompatible type "List[Tuple[BaseItem, BaseItem]]"; expected "List[BaseItem]"
So I've tried item: List[Union[BaseItem, Tuple[BaseItem, BaseItem]]]:
Argument 1 to "exclude_filter" has incompatible type "List[BaseItem]"; expected "List[Union[BaseItem, Tuple[BaseItem, BaseItem]]]"
Then I've tried T = TypeVar("T", BaseItem, Tuple[BaseItem, BaseItem]) and items: List[T] a item: T but I got:
"Tuple[BaseItem, BaseItem]" has no attribute "name"
Well I tried even more obscure combinations but nothing seems to work. What is the correct way to annotate this code?
Well I've found working but ugly solution to this problem and sadly it only works for homogenous lists.
The biggest problem with
Listbeing invariant is this:That is the reason I cannot use
Tuple[BaseItem, ....]inTypeVarand I must explicitely state all possible tuple lengths. It will be OK if I am usingSequencebut due toitems[:]operation I can't.Also there is probably bug in mypy with conditional expression and using
isinstance()so I need to use proper if-else block.