[Tutor] Type annotation errors
Peter Otten
__peter__ at web.de
Thu Jun 4 04:10:41 EDT 2020
boB Stepp wrote:
> As I continue to integrate type annotations with David Beazley's
> recently released course, "Practical Python Programming", I ran into
> another issue. I have pared down a larger function to just what seems
> to be giving the type annotation complaint:
>
> def parse_csv(data_rows: List, has_headers: bool = True) -> List:
> """Parse a CSV file into a list of records."""
> data_rows_iter = iter(data_rows)
> if has_headers:
> headers = next(data_rows_iter)
> records = []
> for row in data_rows_iter:
> if has_headers:
> # Make a dictionary
> record = dict(zip(headers, row))
> else:
> # Otherwise make a tuple
> record = tuple(row)
> records.append(record)
>
> return records
>
> The complaint is: test.py|19 col 22 error| Incompatible types in
> assignment (expression has type "Tuple[Any, ...]", variable has type
> "Dict[Any, Any]")
>
> One of the exercises is to return a list of dictionaries if a csv file
> has headers, but if not to return a list of tuples. mypy apparently
> does not like this. In the "Type hints cheat sheet..." (at
> https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html) in the
> section "When you're puzzled or when things are complicated" it
> discusses the use of "Any":
>
> "Use Any if you don't know the type of something or it's too dynamic
> to write a type for..."
>
> This does not seem particularly complicated to me, so I'm probably
> doing something bad or screwy. Any thoughts?
Let me note that I do not have any experience with mypy.
However, the following
> if has_headers:
> # Make a dictionary
> record = dict(zip(headers, row))
> else:
> # Otherwise make a tuple
> record = tuple(row)
makes every user of classical statically typed languages cringe.
What's the type of record after the above? It's either the type of
dict(zip(headers, row))
or the type of
tuple(row)
I don't know if mypy is not smart enough or just refuses to infer the type
-- in any case you can help it with a declaration.
The most generic would be
record: Any
but making a union from the types in the error message should work too.
record: Union[Tuple[Any, ...], Dict[Any, Any]]
You might try to tighten that a bit -- e. g. requiring the dict keys to be
strings.
There are other things that you may know about the types
- all records of one function run will be either tuples or dicts, never a
mixture
- all iterables in the data_rows list should have the same "length"
- the first item in the data_rows list should be an iterable of strings if
has_headers is true
where I have no idea how it might be explained to a type checker. I fear
that a moderately clean typed soluton would require two separate functions
parse_csv()
parse_csv_with_headers()
> BTW, the above function works.
Hooray to Python's ducktyping ;)
> If I run it with:
>
> with_headers = [["a", "b", "c"], [1, 2, 3]]
> without_headers = [[1, 2, 3]]
>
> my_dict = parse_csv(with_headers)
> my_tuple = parse_csv(without_headers, has_headers=False)
>
> print("My dict =", my_dict)
> print()
> print("My tuple =", my_tuple)
>
> I get my expected result:
>
> bob at Dream-Machine1:~/practical-python/Work$ python3 test.py
> My dict = [{'a': 1, 'b': 2, 'c': 3}]
>
> My tuple = [(1, 2, 3)]
>
More information about the Tutor
mailing list