I'm trying to come up with the perfect function signature for the following function (Python 3.6, mypy 0.521):
def avg(xs):
it = iter(xs)
try:
s = next(it)
i = 1
except StopIteration:
raise ValueError("Cannot average empty sequence")
for x in it:
s += x
i += 1
return s / i
The nice thing about this code, is that it works with, and produces the correct result for iterables of int, float, complex, but also for datetime.timedelta. Problems pop up when trying to add a signature. I've tried the following:
def avg(xs: t.Iterable[t.Any]) -> t.Any: ...
But now, caller needs to cast the result.
def avg(xs: t.Iterable[T]) -> T: ...
This fails because T does not support addition nor division.
N = TypeVar("N", int, float, complex, datetime.timedelta)
def avg(xs: t.Iterable[N]) -> N: ...
Fails because int / int is a float; using // gives the wrong result for almost everything else. Also sucks, because the code should work for other types, as long as addition and division are supported.
N = TypeVar("N", float, complex, datetime.timedelta)
def avg(xs: t.Iterable[N]) -> N: ...
This is almost perfect, but again, if someone later decides to throw quaternions at it, mypy will complain.
...then I was also trying something with abc and typing.overload but that got me nowhere.
What would be the most elegant solution that would pass under mypy --strict?