diff --git a/pandas/core/internals/array_manager.py b/pandas/core/internals/array_manager.py index f2cd6320d885a..157e90d6e8b0a 100644 --- a/pandas/core/internals/array_manager.py +++ b/pandas/core/internals/array_manager.py @@ -1281,6 +1281,14 @@ def apply(self, func, **kwargs): return type(self)([new_array], self._axes) def setitem(self, indexer, value): + """ + Set values with indexer. + + For SingleArrayManager, this backs s[indexer] = value + + See `setitem_inplace` for a version that works inplace and doesn't + return a new Manager. + """ return self.apply_with_block("setitem", indexer=indexer, value=value) def idelete(self, indexer) -> SingleArrayManager: diff --git a/pandas/core/internals/base.py b/pandas/core/internals/base.py index e65318dd29c52..4d3dcb9c4732e 100644 --- a/pandas/core/internals/base.py +++ b/pandas/core/internals/base.py @@ -159,6 +159,18 @@ def array(self): """ return self.arrays[0] # type: ignore[attr-defined] + def setitem_inplace(self, indexer, value) -> None: + """ + Set values with indexer. + + For Single[Block/Array]Manager, this backs s[indexer] = value + + This is an inplace version of `setitem()`, mutating the manager/values + in place, not returning a new Manager (and Block), and thus never changing + the dtype. + """ + self.array[indexer] = value + def interleaved_dtype(dtypes: list[DtypeObj]) -> DtypeObj | None: """ diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 9d35e1e8d4929..7c8b289e6eb87 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -352,6 +352,11 @@ def where(self: T, other, cond, align: bool, errors: str) -> T: ) def setitem(self: T, indexer, value) -> T: + """ + Set values with indexer. + + For SingleBlockManager, this backs s[indexer] = value + """ return self.apply("setitem", indexer=indexer, value=value) def putmask(self, mask, new, align: bool = True): diff --git a/pandas/core/series.py b/pandas/core/series.py index ce986f2dd8038..6efd1f65c2264 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1061,7 +1061,6 @@ def __setitem__(self, key, value) -> None: try: self._set_with_engine(key, value) except (KeyError, ValueError): - values = self._values if is_integer(key) and self.index.inferred_type != "integer": # positional setter if not self.index._should_fallback_to_positional: @@ -1075,7 +1074,8 @@ def __setitem__(self, key, value) -> None: FutureWarning, stacklevel=2, ) - values[key] = value + # this is equivalent to self._values[key] = value + self._mgr.setitem_inplace(key, value) else: # GH#12862 adding a new key to the Series self.loc[key] = value @@ -1107,7 +1107,8 @@ def _set_with_engine(self, key, value) -> None: # error: Argument 1 to "validate_numeric_casting" has incompatible type # "Union[dtype, ExtensionDtype]"; expected "dtype" validate_numeric_casting(self.dtype, value) # type: ignore[arg-type] - self._values[loc] = value + # this is equivalent to self._values[key] = value + self._mgr.setitem_inplace(loc, value) def _set_with(self, key, value): # other: fancy integer or otherwise