Skip to content

Commit 3a5e430

Browse files
jbrockmendelJulianWgs
authored andcommitted
REF: move Series-specific methods from NDFrame (pandas-dev#40776)
1 parent b5b9955 commit 3a5e430

File tree

3 files changed

+113
-83
lines changed

3 files changed

+113
-83
lines changed

pandas/core/frame.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -3416,7 +3416,7 @@ def __getitem__(self, key):
34163416
# - we have a MultiIndex on columns (test on self.columns, #21309)
34173417
if data.shape[1] == 1 and not isinstance(self.columns, MultiIndex):
34183418
# GH#26490 using data[key] can cause RecursionError
3419-
data = data._get_item_cache(key)
3419+
return data._get_item_cache(key)
34203420

34213421
return data
34223422

@@ -3799,6 +3799,43 @@ def _box_col_values(self, values, loc: int) -> Series:
37993799
klass = self._constructor_sliced
38003800
return klass(values, index=self.index, name=name, fastpath=True)
38013801

3802+
# ----------------------------------------------------------------------
3803+
# Lookup Caching
3804+
3805+
def _clear_item_cache(self) -> None:
3806+
self._item_cache.clear()
3807+
3808+
def _get_item_cache(self, item: Hashable) -> Series:
3809+
"""Return the cached item, item represents a label indexer."""
3810+
cache = self._item_cache
3811+
res = cache.get(item)
3812+
if res is None:
3813+
# All places that call _get_item_cache have unique columns,
3814+
# pending resolution of GH#33047
3815+
3816+
loc = self.columns.get_loc(item)
3817+
values = self._mgr.iget(loc)
3818+
res = self._box_col_values(values, loc).__finalize__(self)
3819+
3820+
cache[item] = res
3821+
res._set_as_cached(item, self)
3822+
3823+
# for a chain
3824+
res._is_copy = self._is_copy
3825+
return res
3826+
3827+
def _reset_cacher(self) -> None:
3828+
# no-op for DataFrame
3829+
pass
3830+
3831+
def _maybe_cache_changed(self, item, value: Series) -> None:
3832+
"""
3833+
The object has called back to us saying maybe it has changed.
3834+
"""
3835+
loc = self._info_axis.get_loc(item)
3836+
arraylike = value._values
3837+
self._mgr.iset(loc, arraylike)
3838+
38023839
# ----------------------------------------------------------------------
38033840
# Unsorted
38043841

pandas/core/generic.py

+3-82
Original file line numberDiff line numberDiff line change
@@ -868,8 +868,6 @@ def droplevel(self: FrameOrSeries, level, axis=0) -> FrameOrSeries:
868868
def pop(self, item: Hashable) -> Series | Any:
869869
result = self[item]
870870
del self[item]
871-
if self.ndim == 2:
872-
result._reset_cacher()
873871

874872
return result
875873

@@ -3511,46 +3509,12 @@ def to_csv(
35113509
# ----------------------------------------------------------------------
35123510
# Lookup Caching
35133511

3514-
@final
3515-
def _set_as_cached(self, item, cacher) -> None:
3516-
"""
3517-
Set the _cacher attribute on the calling object with a weakref to
3518-
cacher.
3519-
"""
3520-
self._cacher = (item, weakref.ref(cacher))
3521-
3522-
@final
35233512
def _reset_cacher(self) -> None:
35243513
"""
35253514
Reset the cacher.
35263515
"""
3527-
if hasattr(self, "_cacher"):
3528-
del self._cacher
3529-
3530-
@final
3531-
def _maybe_cache_changed(self, item, value) -> None:
3532-
"""
3533-
The object has called back to us saying maybe it has changed.
3534-
"""
3535-
loc = self._info_axis.get_loc(item)
3536-
arraylike = value._values
3537-
self._mgr.iset(loc, arraylike)
3538-
3539-
@final
3540-
@property
3541-
def _is_cached(self) -> bool_t:
3542-
"""Return boolean indicating if self is cached or not."""
3543-
return getattr(self, "_cacher", None) is not None
3544-
3545-
@final
3546-
def _get_cacher(self):
3547-
"""return my cacher or None"""
3548-
cacher = getattr(self, "_cacher", None)
3549-
if cacher is not None:
3550-
cacher = cacher[1]()
3551-
return cacher
3516+
raise AbstractMethodError(self)
35523517

3553-
@final
35543518
def _maybe_update_cacher(
35553519
self, clear: bool_t = False, verify_is_copy: bool_t = True
35563520
) -> None:
@@ -3565,32 +3529,15 @@ def _maybe_update_cacher(
35653529
verify_is_copy : bool, default True
35663530
Provide is_copy checks.
35673531
"""
3568-
cacher = getattr(self, "_cacher", None)
3569-
if cacher is not None:
3570-
ref = cacher[1]()
3571-
3572-
# we are trying to reference a dead referent, hence
3573-
# a copy
3574-
if ref is None:
3575-
del self._cacher
3576-
else:
3577-
if len(self) == len(ref):
3578-
# otherwise, either self or ref has swapped in new arrays
3579-
ref._maybe_cache_changed(cacher[0], self)
3580-
else:
3581-
# GH#33675 we have swapped in a new array, so parent
3582-
# reference to self is now invalid
3583-
ref._item_cache.pop(cacher[0], None)
35843532

35853533
if verify_is_copy:
35863534
self._check_setitem_copy(stacklevel=5, t="referent")
35873535

35883536
if clear:
35893537
self._clear_item_cache()
35903538

3591-
@final
35923539
def _clear_item_cache(self) -> None:
3593-
self._item_cache.clear()
3540+
raise AbstractMethodError(self)
35943541

35953542
# ----------------------------------------------------------------------
35963543
# Indexing Methods
@@ -3886,26 +3833,6 @@ class animal locomotion
38863833
def __getitem__(self, item):
38873834
raise AbstractMethodError(self)
38883835

3889-
@final
3890-
def _get_item_cache(self, item):
3891-
"""Return the cached item, item represents a label indexer."""
3892-
cache = self._item_cache
3893-
res = cache.get(item)
3894-
if res is None:
3895-
# All places that call _get_item_cache have unique columns,
3896-
# pending resolution of GH#33047
3897-
3898-
loc = self.columns.get_loc(item)
3899-
values = self._mgr.iget(loc)
3900-
res = self._box_col_values(values, loc).__finalize__(self)
3901-
3902-
cache[item] = res
3903-
res._set_as_cached(item, self)
3904-
3905-
# for a chain
3906-
res._is_copy = self._is_copy
3907-
return res
3908-
39093836
def _slice(self: FrameOrSeries, slobj: slice, axis=0) -> FrameOrSeries:
39103837
"""
39113838
Construct a slice of this container.
@@ -3931,7 +3858,6 @@ def _set_is_copy(self, ref: FrameOrSeries, copy: bool_t = True) -> None:
39313858
assert ref is not None
39323859
self._is_copy = weakref.ref(ref)
39333860

3934-
@final
39353861
def _check_is_chained_assignment_possible(self) -> bool_t:
39363862
"""
39373863
Check if we are a view, have a cacher, and are of mixed type.
@@ -3943,12 +3869,7 @@ def _check_is_chained_assignment_possible(self) -> bool_t:
39433869
single-dtype meaning that the cacher should be updated following
39443870
setting.
39453871
"""
3946-
if self._is_view and self._is_cached:
3947-
ref = self._get_cacher()
3948-
if ref is not None and ref._is_mixed_type:
3949-
self._check_setitem_copy(stacklevel=4, t="referent", force=True)
3950-
return True
3951-
elif self._is_copy:
3872+
if self._is_copy:
39523873
self._check_setitem_copy(stacklevel=4, t="referent")
39533874
return False
39543875

pandas/core/series.py

+72
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
overload,
2020
)
2121
import warnings
22+
import weakref
2223

2324
import numpy as np
2425

@@ -1141,6 +1142,77 @@ def _set_value(self, label, value, takeable: bool = False):
11411142

11421143
self._set_values(loc, value)
11431144

1145+
# ----------------------------------------------------------------------
1146+
# Lookup Caching
1147+
1148+
@property
1149+
def _is_cached(self) -> bool:
1150+
"""Return boolean indicating if self is cached or not."""
1151+
return getattr(self, "_cacher", None) is not None
1152+
1153+
def _get_cacher(self):
1154+
"""return my cacher or None"""
1155+
cacher = getattr(self, "_cacher", None)
1156+
if cacher is not None:
1157+
cacher = cacher[1]()
1158+
return cacher
1159+
1160+
def _reset_cacher(self) -> None:
1161+
"""
1162+
Reset the cacher.
1163+
"""
1164+
if hasattr(self, "_cacher"):
1165+
# should only get here with self.ndim == 1
1166+
del self._cacher
1167+
1168+
def _set_as_cached(self, item, cacher) -> None:
1169+
"""
1170+
Set the _cacher attribute on the calling object with a weakref to
1171+
cacher.
1172+
"""
1173+
self._cacher = (item, weakref.ref(cacher))
1174+
1175+
def _clear_item_cache(self) -> None:
1176+
# no-op for Series
1177+
pass
1178+
1179+
def _check_is_chained_assignment_possible(self) -> bool:
1180+
"""
1181+
See NDFrame._check_is_chained_assignment_possible.__doc__
1182+
"""
1183+
if self._is_view and self._is_cached:
1184+
ref = self._get_cacher()
1185+
if ref is not None and ref._is_mixed_type:
1186+
self._check_setitem_copy(stacklevel=4, t="referent", force=True)
1187+
return True
1188+
return super()._check_is_chained_assignment_possible()
1189+
1190+
def _maybe_update_cacher(
1191+
self, clear: bool = False, verify_is_copy: bool = True
1192+
) -> None:
1193+
"""
1194+
See NDFrame._maybe_update_cacher.__doc__
1195+
"""
1196+
cacher = getattr(self, "_cacher", None)
1197+
if cacher is not None:
1198+
assert self.ndim == 1
1199+
ref: DataFrame = cacher[1]()
1200+
1201+
# we are trying to reference a dead referent, hence
1202+
# a copy
1203+
if ref is None:
1204+
del self._cacher
1205+
else:
1206+
if len(self) == len(ref):
1207+
# otherwise, either self or ref has swapped in new arrays
1208+
ref._maybe_cache_changed(cacher[0], self)
1209+
else:
1210+
# GH#33675 we have swapped in a new array, so parent
1211+
# reference to self is now invalid
1212+
ref._item_cache.pop(cacher[0], None)
1213+
1214+
super()._maybe_update_cacher(clear=clear, verify_is_copy=verify_is_copy)
1215+
11441216
# ----------------------------------------------------------------------
11451217
# Unsorted
11461218

0 commit comments

Comments
 (0)