The intention is to overload the function and allow for multiple types of input, with user-defined coherent output.
I thus set a Type[TypeVar]
of str
or datetime.date
(second function argument, with default value=str
) where the function would output the relevant TypeVar
.
The following is the basic function (that I would further extend once this version is fixed):
from typing import TypeVar, Type
from datetime import date, datetime
DateOutType = TypeVar('DateOutType', str, date)
def date2str(t: date, out_format: Type[DateOutType]=Type[str]) -> DateOutType:
''' Converts datetime.date to string (YYYY-MM-DD) or datetime.date output.
'''
if out_format is str:
return t.strftime('%Y-%m-%d')
elif isinstance(t, datetime):
return t.date()
else:
return t
# Usage example:
dt = datetime.now()
res = date2str(dt, out_format=date)
assert type(res) == date
res = date2str(dt.date(), out_format=str)
assert type(res) == str
mypy gives error on the return statements (the TypeVar doesn't seem to work as I would have expected):
first return statement: error: Incompatible return value type (got "str", expected "date")
second return statement: error: Incompatible return value type (got "date", expected "str")
third return statement: error: Incompatible return value type (got "date", expected "str")
Any ideas? Is there a better way to write this code with proper type annotation?
The issue here is that mypy does not understand expressions of the form some_type is str
or type(some_value) is str
. You need to do either issubclass(some_type, str)
or isinstance(some_value, str)
instead.
That said, it would be more cleaner in this case to just make two different functions instead:
def date_to_str(t: date) -> str:
return t.strftime('%Y-%m-%d')
def normalize_date(t: date) -> date:
if isinstance(t, datetime):
return t.date()
return t
This ends up being fewer characters for your users to type since they don't need to keep including out_format=date
or out_format=str
everywhere. It's also less misleading: your original function doesn't actually always return a str, so calling it date2str
is kind of confusing.
It would also probably be a good idea to just delete normalize_date
entirely: datetime is a subclass of date, which means it's valid to use a datetime in any place that expects just a date. This means there really shouldn't be a need to explicitly convert a datetime into a date
For example, the following two prints would do the exact same thing (and would type check, according to mypy):
from datetime import datetime
dt = datetime.now()
print(date_to_str(dt))
print(date_to_str(normalize_date(dt)))