Usage

There are two ways to set per-type sum start values:

Approach Strength
Decorator Performance
The __sum_start__ class attribute Heritable defaults

Both keep things simple:

Decorator

This is a more elaborate version of GitHub README.md's example.

Import Purpose
better_sum.sum_starts_at_instance Create a default instance
better_sum.sum Type-aware replacement for Python's sum

This Since the decorator creates an instance immediately, this is beneficial if creating

from typing import NamedTuple
from better_sum import sum, sum_starts_at_instance

@sum_starts_at_instance(0.0, 0.0)
class Vec2(NamedTuple):
    """Non-addition operations omitted for brevity."""

    x: float = 0.0
    y: float = 0.0

    # For more strictness, you could type other as Self
    def __radd__(self, other: tuple[float, float]) -> Vec2:
        """Support for passing ints is no longer needed!"""
        other_x, other_y = other
        return self.__class__(other_x + self.x, other_y + self.y)

    def __add__(self, other: tuple[float, float]) -> Vec2:
        other_x, other_y = other
        return self.__class__(self.x + other_x, self.y + other_y)


if __name__ == "__main__":
     print("Same type addition  :", sum([Vec2(1.0, 1.0), Vec2(1.0, 1.0)]))
     print("Mixed type addition :", sum([Vec2(1.0, 1.0), (1.0, 1.0)]))

Note

The mixed-type addition above only works when a Vec2 is the first item in the iterable.

Class attribute

You can also set a __sum_start__ class attribute.

Instead of creating a default instance immediately like the decorator, better_sum.sum will create it the first time it encounters an instance of the type.

Instead of creating a default instance up front, this approach waits until the first time an instance of a type is passed. This may be worse when object creation is slow or if you have a lot of summable types.

from __future__ import annotations
from better_sum import sum

class Vec2:

    __sum_start__ = (0.0, 0.0)

    def __init__(self, x: float = 0.0, y: float = 0.0):
        self._values: list[float] = [x, y]

    @property
    def x(self) -> float:
        return self._values[0]

    @property
    def y(self) -> float:
        return self._values[1]

    def __radd__(self, other: tuple[float, float]) -> Vec2:
        """Support for passing ints is no longer needed!"""
        other_x, other_y = other
        return self.__class__(other_x + self[0], other_y + self[1])

    def __add__(self, other: tuple[float, float]) -> Vec2:
        other_x, other_y = other
        return self.__class__(self[0] + other_x, self[1] + other_y)


if __name__ == "__main__":
     print("Same type addition  :", sum([Vec2(1.0, 1.0), Vec2(1.0, 1.0)]))
     print("Mixed type addition :", sum([Vec2(1.0, 1.0), (1.0, 1.0)]))

As above, the mixed-type addition only works when a Vec2 is the first item summed.

What if no default type is registered?

For backward-compatibility, better_sum.sum will use 0 as the default if no default is found for a type.