# mypy sum error

Recently, I’ve been exploring the type hints functionality in Python. The other day, I ran across what I think is a bug (already known) in mypy.

Here is a minimal working example of the issue I came across:

 1 2 3 4  from fractions import Fraction my_value = 1 + sum([Fraction(k + 1, 5 ** k) for k in range(5)]) print(f"The result is {my_value}.")

This program runs as you would expect.

% python example.py
The result is 64/25.


However, the type-check fails!

% mypy example.py
example.py:3: error: List comprehension has incompatible type List[Fraction]; expected List[int]
Found 1 error in 1 file (checked 1 source file)


At this point, I was really confused, but after searching for the error, I came across an issue on GitHub that reported this. In the discussion, someone explained that the problem is that mypy isn’t taking into account the __radd__ method. (In general, x + y is shorthand for x.__add__(y). However, if x.__add__ doesn’t know how to deal with y, then Python tries to use y.__radd__(x) instead.) Following the example in the discussion there, I modified the program as follows:

 3  my_value = Fraction(1) + sum([Fraction(k + 1, 5 ** k) for k in range(5)])

The modified version type-checked okay.

At this point, I decided to come up with what my doctoral advisor would call a “Toy Example”:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38  from __future__ import annotations # Allow self-referential type hints from typing import Union class A: def __init__(self: A, value: int) -> None: self.value = value def __repr__(self: A) -> str: return f"{self.__class__.__name__}({self.value!r})" def __add__(self: A, other: Union[A, int]) -> A: print(f"Calling {self!r}.__add__({other!r})") if isinstance(other, A): return self.__class__(self.value + other.value) elif isinstance(other, int): return self.__class__(self.value + other) else: return NotImplemented def __radd__(self: A, other: int) -> A: print(f"Calling {self!r}.__radd__({other!r})") if isinstance(other, int): return self.__class__(self.value + other) else: return NotImplemented if __name__ == "__main__": values_to_sum = [A(k) for k in range(1, 5)] print(f"Computation #1: {sum(values_to_sum)=}") print(f"Computation #2: {A(5) + sum(values_to_sum)=}") print(f"Computation #3: {5 + sum(values_to_sum)=}") print(f"Computation #4: {sum(values_to_sum) + A(5)=}") print(f"Computation #5: {sum(values_to_sum) + 5=}") print(f"Computation #6: {A(5) + A(6)=}") print(f"Computation #7 {5 + A(6)=}") print(f"Computation #8 {A(5) + 6=}")

Then it executes just fine, but mypy is upset about line 33, in which A(10).__radd__(5) is called. (In the output below, the Calling ... text is output before the result of the computation.)

% python3 example.py
Computation #1: sum(values_to_sum)=A(10)
Computation #2: A(5) + sum(values_to_sum)=A(15)
Computation #3: 5 + sum(values_to_sum)=A(15)
Computation #4: sum(values_to_sum) + A(5)=A(15)
Computation #5: sum(values_to_sum) + 5=A(15)
Computation #6: A(5) + A(6)=A(11)
Computation #7 5 + A(6)=A(11)

If you remove line 33 in the script (the one that evaluates 5 + sum(values_to_sum)), then mypy has no problem!
% mypy example.py

Note that line 33 isn’t the only place that __radd__ ends up being called. It’s called every time sum(values_to_sum) is evaluated (since you start each sum with a value of the integer 0). It’s also called in line 37, in the computation of 5 + A(6). These other invocations of __radd__ do not mess up mypy. It’s only messed up when you __radd__ the result of a sum.