Trying to learn to type hint in Python. Given these two functions:
from typing import Union, TextIO
def myfunc_ok(file: TextIO):
mydump = file.read()
print(mydump)
def myfunc_error(file: Union[str, TextIO]):
mydump = file.read()
print(mydump)
First one is ok to mypy, but it complains about the second one with an error
Item "str" of "Union[str, TextIO]" has no attribute "read"
Am I using type hinting incorrenctly in this case? (Using python3.7 with mypy 0.610, also tested with py3.6)
Your signature
def myfunc_error(file: Union[str, TextIO]):
...
says that file
parameter can be str
or TextIO
, after that in function body you are trying to access .read
attribute of file
object, but in case of file
being str
there is no such attribute hence the error.
You have at least 3 possibilities here:
file
being of type str
and replace Union[str, TextIO]
with TextIO
add explicit type checking using isinstance
built-in in function body like
import io
...
def myfunc_error(file: Union[str, TextIO]):
if isinstance(file, io.TextIOWrapper):
mydump = file.read()
else:
# assuming ``file`` is a required object already
mydump = file
print(mydump)
this may become difficult to maintain in the long term
write 2 different functions for given task: one for str
parameter and one for TextIO
parameter like
def myfunc_error_str_version(file: str):
mydump = file
print(mydump)
def myfunc_error_text_io_version(file: TextIO):
mydump = file.read()
print(mydump)
this may cause a lot of naming problems (but it depends on the use-case)
The last approach can be improved using functools.singledispatch
decorator: in short this will allow us to define a generic function & use a name myfunc_error
with overloads called based on the type of first positional argument (file
in our case):
import io
from functools import singledispatch
from typing import TextIO
@singledispatch
def myfunc_error(file: str):
mydump = file
print(mydump)
# using ``typing.TextIO`` will not work because it's just an interface for type annotations,
# "real" types are located at ``io`` module
@myfunc_error.register(io.TextIOWrapper)
def _(file: TextIO):
mydump = file.read()
print(mydump)
Note: we can use any name we want instead of _
except myfunc_error
, for the latter mypy
will raise a name conflict error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With