Skip to content

feat(series): #1098 __add__ and __mul__ #1275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

cmp0xff
Copy link
Contributor

@cmp0xff cmp0xff commented Jul 13, 2025

This PR is motivated by #1273 (review) and partially addresses #1098 and updates __add__ and __mul__.

The task proved to be tricky, and I have the feeling that pyright and mypy do not support self typing very well. I did not make __sub__ and __div__ as well as __truediv__ work.

Comprehensive plan (maybe in another PR)

Inspired by #1093, one can draft the following comprehensive plan for all possible combinations of the left arithmetic operand, the binary arithmetic operator and the right arithmetic operand.

Left operand

  • Series[Any] and Series[Unknown]
  • Series[int]
  • Series[float]
  • Series[complex]

Binary arithmetic operators

Include their right-version and functional version. In add we give a full example. We follow the official documentation.

  • __add__, __radd__, add, radd
  • __truediv__
  • __floordiv__
  • __pow__
  • __mod__
  • __mul__
  • __sub__

Right operand

  • Scalar
  • Sequence
  • numpy arrays
  • Series

Draft of a plan

  • We can have 4 ~ 5 test scripts for each left operand
  • In each test script, we can have 7 test functions for each binary arithmetic operator
  • In each test function, we test on all right operands. Do not forget the possible four variations, see the example of add.

Checklist

  • Tests added: Please use assert_type() to assert the type of any return value

@cmp0xff cmp0xff marked this pull request as ready for review July 13, 2025 16:32
Copy link
Member

@loicdiridollou loicdiridollou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like great progress here!


def test_types_scalar_arithmetic() -> None:
# TODO: assert_type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to keep the TODO here?

Copy link
Contributor Author

@cmp0xff cmp0xff Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you for jumping in. I quickly went through #1093 and recognised that it was important to define a scope within which we want to address the (original) issue.

I think the current MR has addressed most of the original #1098, isn't it?

The TODOs are for a more comprehensive plan, to rectify all possible arithmetic operations, which I think is beyond the scope of the original #1098.


check(assert_type(s - 1.0, pd.Series), pd.Series, np.floating)
check(assert_type(1.0 - s, pd.Series), pd.Series, np.floating)
# check(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to keep the code here it may be worth adding a TODO and a reason for when we can uncomment it

_T_COMPLEX = TypeVar("_T_COMPLEX", bound=complex)
_T_INT_FLOAT = TypeVar("_T_INT_FLOAT", bound=int | float)
_T_FLOAT_COMPLEX = TypeVar("_T_FLOAT_COMPLEX", bound=float | complex)
_T_INT_FLOAT_COMPLEX = TypeVar("_T_INT_FLOAT_COMPLEX", bound=int | float | complex)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't _T_FLOAT_COMPLEX, _T_COMPLEX, and _T_INT_FLOAT_COMPLEX from a typing persepctive the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? They have different bounds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A function that accepts a complex, is also assumed (from a typing perspective) to accept a float or an int https://peps.python.org/pep-3141/#abstract even though they aren't sub-classes at runtime

self: Series[_T_COMPLEX],
other: (
_T_INT_FLOAT_COMPLEX
| Sequence[_T_INT_FLOAT_COMPLEX]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sequence[_T_INT_FLOAT_COMPLEX] can probably be Sequence[complex]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was that Series[float] + Series[complex] -> Series[complex] and Series[comples] + Series[float] -> Series[complex] are two different things. It addresses the latter point here.

@cmp0xff cmp0xff marked this pull request as draft July 14, 2025 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants