The Problem
Many of you are probably familiar with namedtuple
(and if not, you should).
However, in this age of type-hints (if you don’t know what it is, you should 😉 ), namedtuple
is proving less useful.
Consider the following code:
def expect_str(str_arg):
# type: (typing.Text) -> None
pass
def expect_int(num_arg):
# type: (int) -> None
pass
ItemCount = collections.namedtuple('ItemCount', ['name', 'count'])
item_count = ItemCount('aaa', 42)
expect_str(item_count.name)
item_count = ItemCount(11, 42)
expect_str(item_count.name)
When asking PyCharm what is the type of item_count.name
in line 14 we get:
But on line 16, the type is already int
:
At this point, PyCharm will complain that expect_str
function expects a string:
The worse news is that mypy
is not even catching this, because namedtuple
s are simply untyped, so item_count.name
is of type Any
(when using mypy
utilize reveal_type
to understand deductions).
All of that is rather icky if you ask me. I get that Python is dynamic and all, but same class having totally unexpected types is hard to work with, and the fact PyCharm’s intelli-sense keeps changing its mind is confusing. I preferred it being treated the same as mypy
.
BTW, If you add a method that expects ItemCount
, the deduced type within that function, is indeed Any
. Try it!
Enter:typing.NamedTuple
!
With typing.NamedTuple
you can easily define the types for your tuple components:
from typing import Text, NamedTuple
TypedItemCount = NamedTuple('TypedItemCount',
[('name', Text), ('count', int)])
typed_item_count = TypedItemCount('abc', 42)
no_good_item_count = TypedItemCount(24, 42)
Now we get the warning at the point we really should:
And with mypy:
guyarad@guyarad-mbp:$ mypy test_namedtuple_hints.py
test_namedtuple_hints.py:25: error: Argument 1 to "TypedItemCount" has
incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)
Voila!