Skip to content

REF: avoid mutating Series._values directly in setitem but defer to Manager method #41879

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

19 changes: 17 additions & 2 deletions pandas/core/internals/array_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1286,8 +1286,23 @@ def apply(self, func, **kwargs):
new_array = getattr(self.array, func)(**kwargs)
return type(self)([new_array], self._axes)

def setitem(self, indexer, value):
return self.apply_with_block("setitem", indexer=indexer, value=value)
def setitem(self, indexer, value, inplace: bool = False):
"""
Set values with indexer.

For SingleArrayManager, this backs s[indexer] = value

Parameters
----------
indexer, value
inplace : bool, default False
If True (for a Series), mutate the manager/values in place, not
returning a new Manager (and thus never changing the dtype).
"""
if inplace:
self.arrays[0][indexer] = value
else:
return self.apply_with_block("setitem", indexer=indexer, value=value)

def idelete(self, indexer) -> SingleArrayManager:
"""
Expand Down
21 changes: 19 additions & 2 deletions pandas/core/internals/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,25 @@ def where(self: T, other, cond, align: bool, errors: str) -> T:
errors=errors,
)

def setitem(self: T, indexer, value) -> T:
return self.apply("setitem", indexer=indexer, value=value)
def setitem(self: T, indexer, value, inplace: bool = False) -> T:
"""
Set values with indexer.

For SingleBlockManager, this backs s[indexer] = value

Parameters
----------
indexer, value
inplace : bool, default False
If True (for a Series), mutate the block/values in place, not
returning a new Manager/Block (and thus never changing the dtype).
"""
if inplace:
# only passed for Series (single block)
assert self.ndim == 1
self._block.values[indexer] = value
else:
return self.apply("setitem", indexer=indexer, value=value)

def putmask(self, mask, new, align: bool = True):

Expand Down
5 changes: 2 additions & 3 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,10 +1064,9 @@ 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
values[key] = value
self._mgr.setitem(key, value, inplace=True)
else:
# GH#12862 adding a new key to the Series
self.loc[key] = value
Expand Down Expand Up @@ -1099,7 +1098,7 @@ 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
self._mgr.setitem(loc, value, inplace=True)

def _set_with(self, key, value):
# other: fancy integer or otherwise
Expand Down