I have a function that takes either a list of five ints or five ints as a tuple (*argv).
Here's my function heading:
def __init__(self, *argv: Union[int, list]) -> None:
Later in this function I check the contents of this tuple to see if it's a list or five individual ints.
if type(argv[0]) == int:
self._l1 = [argv[0], argv[1], argv[2], argv[3], argv[4]]
else:
self._l1 = argv[0]
By this point in the code l1 is a list. self._l1 is definitely a list, it's no longer an int, it's a list.
However later in my code when I run this line:
self._myvar = self._l1.count(1)
I am returned this error from MyPy
Incompatible types in assignment (expression has type "Union[int, List[Any]]", variable has type "List[Union[int, List[Any]]]")
Am I typing this wrong, what do I need to type this as? I've tried so many different types and keep getting errors.
As far as I can tell though my input is a tuple that will either contain a list of ints or five ints. I'd assume it's something like Union[Tuple[List[int]], Tuple[int, ...]] or just Union[List[int], int] or Union[List[int], Tuple[int, ...]], or something similar, but none of these are working for me.
CodePudding user response:
Wasn't able to find why you version isn't working (as documentation states it should work for type(...) is
syntax, but in my case changing type to if isinstance(argv[0], int):
removed your mypy error.
CodePudding user response:
I recommend ensuring that _li
is always type List[int]
.
I haven't tested the below code, but cast
should work:
if isinstance(argv[0], int):
self._l1 = [cast(int, argv[0]), cast(int, argv[1]), cast(int, argv[2]), cast(int, argv[3]), cast(int, argv[4])]
else:
self._l1 = cast(List[int], argv[0])
print(type(self._li))
And where you declare _li and _myvar:
_li: List[int]
_myvar: List[int]
CodePudding user response:
Why not use typing.overload
?
It would look something like this:
from __future__ import annotations
from typing import overload
class MyClass:
@overload
def __init__(self: MyClass, a: int, b: int, c: int, d: int, e: int) -> None:
...
@overload
def __init__(self: MyClass, abcde: tuple[int,int,int,int,int]) -> None:
...
def __init__(self, a, *bcde):
if bcde:
b, c, d, e = *bcde
# etc
else:
a, b, c, d, e = *a
# etc
CodePudding user response:
So you defined argv
as a sequence with the *
, which could contain the int values, or a list of the int values. So what you really want to do is "flatten" the sequence.
def flatten(alist):
"""Flatten a nested set of lists into one list."""
rv = []
for val in alist:
if isinstance(val, (list, tuple)):
rv.extend(flatten(val))
else:
rv.append(val)
return rv
class MyClass:
def __init__(self, *argv: Union[int, list]):
# argv is now a tuple of Union[int, list], or List[Union[int, List]], as the compiler says.
self._l1 = flatten(argv)
# Now, you want to verify they are all ints, and that there are five.
if len(self._l1) != 5:
raise ValueError("Need five ints")
if not all(isinstance(o, int) for o in self._l1):
raise ValueError("not sequence of ints")
# Now you have a single list of 5 ints.