Skip to content

Commit 6a4692f

Browse files
committed
1 parent b4235d0 commit 6a4692f

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

docs/philosophy.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,43 @@ The type `TimestampSeries` is the result of creating a series from `pd.to_dateti
6161
the type `TimedeltaSeries` is the result of subtracting two `TimestampSeries` as well as
6262
the result of `pd.to_timedelta()`.
6363

64+
### Generic Series have restricted arithmetic
65+
66+
Consider the following Series from a DataFrame:
67+
68+
```python
69+
import pandas as pd
70+
from typing_extensions import reveal_type
71+
from typing import TYPE_CHECKING, cast
72+
73+
if TYPE_CHECKING:
74+
from pandas.core.series import TimestampSeries # noqa: F401
75+
76+
77+
frame = pd.DataFrame({"timestamp": [pd.Timestamp(2025, 8, 26)], "tag": ["one"], "value": [1.0]})
78+
values = frame["value"]
79+
reveal_type(values) # type checker: Series[Any], runtime: Series
80+
new_values = values + 2
81+
82+
timestamps = frame["timestamp"]
83+
reveal_type(timestamps) # type checker: Series[Any], runtime: Series
84+
reveal_type(timestamps - pd.Timestamp(2025, 7, 12)) # type checker: Unknown and error, runtime: Series
85+
reveal_type(cast("TimestampSeries", timestamps) - pd.Timestamp(2025, 7, 12)) # type checker: TimedeltaSeries, runtime: Series
86+
87+
tags = frame["tag"]
88+
reveal_type("suffix" + tags) # type checker: Never, runtime: Series
89+
```
90+
91+
Since they are taken from a DataFrame, all three of them, `values`, `timestamps`
92+
and `tags`, are recognized by type checkers as `Series[Any]`. The code snippet
93+
runs fine at runtime. In the stub for type checking, however, we restrict
94+
generic Series to perform arithmetic operations only with numeric types, and
95+
give `Series[Any]` for the results. For `Timedelta`, `Timestamp`, `str`, etc.,
96+
arithmetic is restricted to `Series[Any]` and the result is either undefined,
97+
showing `Unknown` and errors, or `Never`. Users are encouraged to cast such
98+
generic Series to ones with concrete types, so that type checkers can provide
99+
meaningful results.
100+
64101
### Interval is Generic
65102

66103
A pandas `Interval` can be a time interval, an interval of integers, or an interval of

tests/test_frame.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4441,6 +4441,8 @@ def test_frame_setitem_na() -> None:
44414441
df.loc[ind, :] = pd.NA
44424442
df.iloc[[0, 2], :] = pd.NA
44434443

4444+
# reveal_type(df["y"]) gives Series[Any], so we have to cast to tell the
4445+
# type checker what kind of type it is when adding to a Timedelta
44444446
df["x"] = cast("TimestampSeries", df["y"]) + pd.Timedelta(days=3)
44454447
df.loc[ind, :] = pd.NaT
44464448
df.iloc[[0, 2], :] = pd.NaT

0 commit comments

Comments
 (0)