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

16 changes: 16 additions & 0 deletions pandas/core/internals/array_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,8 +1287,24 @@ def apply(self, func, **kwargs):
return type(self)([new_array], self._axes)

def setitem(self, indexer, value):
Copy link
Contributor

Choose a reason for hiding this comment

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

can you type annotate the return values at the very least on these (Any is fine) just indicative

Copy link
Member Author

Choose a reason for hiding this comment

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

This is an existing method, all other Manager methods with general indexer argument are not yet typed, so I would prefer to leave that for a typing-specific PR (I also thought that we generally want to avoid adding Any for "not yet typed" unless it's really "any").

Copy link
Contributor

Choose a reason for hiding this comment

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

kk

"""
Set values with indexer.

For SingleArrayManager, this backs s[indexer] = value
"""
return self.apply_with_block("setitem", indexer=indexer, value=value)

def setitem_inplace(self, indexer, value):
"""
Set values with indexer.

For SingleArrayManager, this backs s[indexer] = value.

This is an inplace version of `setitem`, mutating the manager/values
in place, not returning a new Manager (and thus never changing the dtype).
"""
self.arrays[0][indexer] = value

def idelete(self, indexer) -> SingleArrayManager:
"""
Delete selected locations in-place (new array, same ArrayManager)
Expand Down
17 changes: 17 additions & 0 deletions pandas/core/internals/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -1630,6 +1635,18 @@ def get_slice(self, slobj: slice, axis: int = 0) -> SingleBlockManager:
new_index = self.index._getitem_slice(slobj)
return type(self)(block, new_index)

def setitem_inplace(self, indexer, value):
"""
Set values with indexer.

For SingleBlockManager, this backs s[indexer] = value

This is an inplace version of `setitem`, mutating the block/values
in place, not returning a new Manager/Block (and thus never changing
the dtype).
"""
self._block.values[indexer] = value

@property
def index(self) -> Index:
return self.axes[0]
Expand Down
7 changes: 4 additions & 3 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,10 +1064,10 @@ 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
# 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
Expand Down Expand Up @@ -1099,7 +1099,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
Expand Down