Skip to content

Commit 50470d5

Browse files
jbrockmendeljreback
authored andcommitted
Make _freq/freq/tz/_tz/dtype/_dtype/offset/_offset all inherit reliably (#24517)
1 parent e4cd00b commit 50470d5

File tree

7 files changed

+61
-100
lines changed

7 files changed

+61
-100
lines changed

pandas/core/arrays/datetimelike.py

-2
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,6 @@ def _time_shift(self, periods, freq=None):
11381138
freq = frequencies.to_offset(freq)
11391139
offset = periods * freq
11401140
result = self + offset
1141-
if hasattr(self, 'tz'):
1142-
result._tz = self.tz
11431141
return result
11441142

11451143
if periods == 0:

pandas/core/arrays/datetimes.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ class DatetimeArrayMixin(dtl.DatetimeLikeArrayMixin,
210210
# Constructors
211211

212212
_attributes = ["freq", "tz"]
213-
_tz = None
213+
_dtype = None # type: Union[np.dtype, DatetimeTZDtype]
214214
_freq = None
215215

216216
@classmethod
@@ -231,8 +231,13 @@ def _simple_new(cls, values, freq=None, tz=None):
231231
result = object.__new__(cls)
232232
result._data = values
233233
result._freq = freq
234-
tz = timezones.maybe_get_tz(tz)
235-
result._tz = timezones.tz_standardize(tz)
234+
if tz is None:
235+
dtype = _NS_DTYPE
236+
else:
237+
tz = timezones.maybe_get_tz(tz)
238+
tz = timezones.tz_standardize(tz)
239+
dtype = DatetimeTZDtype('ns', tz)
240+
result._dtype = dtype
236241
return result
237242

238243
def __new__(cls, values, freq=None, tz=None, dtype=None, copy=False,
@@ -399,9 +404,7 @@ def dtype(self):
399404
If the values are tz-aware, then the ``DatetimeTZDtype``
400405
is returned.
401406
"""
402-
if self.tz is None:
403-
return _NS_DTYPE
404-
return DatetimeTZDtype('ns', self.tz)
407+
return self._dtype
405408

406409
@property
407410
def tz(self):
@@ -411,10 +414,10 @@ def tz(self):
411414
Returns
412415
-------
413416
datetime.tzinfo, pytz.tzinfo.BaseTZInfo, dateutil.tz.tz.tzfile, or None
414-
Returns None when the array is tz-naive.
417+
Returns None when the array is tz-naive.
415418
"""
416419
# GH 18595
417-
return self._tz
420+
return getattr(self._dtype, "tz", None)
418421

419422
@tz.setter
420423
def tz(self, value):

pandas/core/indexes/datetimelike.py

+13
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ class DatetimeIndexOpsMixin(ExtensionOpsMixin):
7070
_maybe_mask_results = ea_passthrough("_maybe_mask_results")
7171
__iter__ = ea_passthrough("__iter__")
7272

73+
@property
74+
def freq(self):
75+
return self._eadata.freq
76+
77+
@freq.setter
78+
def freq(self, value):
79+
# validation is handled by _eadata setter
80+
self._eadata.freq = value
81+
7382
@property
7483
def freqstr(self):
7584
return self._eadata.freqstr
@@ -98,6 +107,10 @@ def wrapper(self, other):
98107
wrapper.__name__ = '__{}__'.format(op.__name__)
99108
return wrapper
100109

110+
@property
111+
def _ndarray_values(self):
112+
return self._eadata._ndarray_values
113+
101114
# ------------------------------------------------------------------------
102115

103116
def equals(self, other):

pandas/core/indexes/datetimes.py

+29-38
Original file line numberDiff line numberDiff line change
@@ -321,11 +321,10 @@ def _simple_new(cls, values, name=None, freq=None, tz=None, dtype=None):
321321

322322
dtarr = DatetimeArray._simple_new(values, freq=freq, tz=tz)
323323
result = object.__new__(cls)
324-
result._data = dtarr._data
325-
result._freq = dtarr.freq
326-
result._tz = dtarr.tz
324+
result._eadata = dtarr
327325
result.name = name
328326
# For groupby perf. See note in indexes/base about _index_data
327+
# TODO: make sure this is updated correctly if edited
329328
result._index_data = result._data
330329
result._reset_identity()
331330
return result
@@ -345,19 +344,6 @@ def _values(self):
345344
else:
346345
return self.values
347346

348-
@property
349-
def tz(self):
350-
# GH 18595
351-
return self._tz
352-
353-
@tz.setter
354-
def tz(self, value):
355-
# GH 3746: Prevent localizing or converting the index by setting tz
356-
raise AttributeError("Cannot directly set timezone. Use tz_localize() "
357-
"or tz_convert() as appropriate")
358-
359-
tzinfo = tz
360-
361347
@property
362348
def size(self):
363349
# TODO: Remove this when we have a DatetimeTZArray
@@ -416,15 +402,18 @@ def __setstate__(self, state):
416402
data = np.empty(nd_state[1], dtype=nd_state[2])
417403
np.ndarray.__setstate__(data, nd_state)
418404

405+
freq = own_state[1]
406+
tz = timezones.tz_standardize(own_state[2])
407+
dtarr = DatetimeArray._simple_new(data, freq=freq, tz=tz)
408+
419409
self.name = own_state[0]
420-
self._freq = own_state[1]
421-
self._tz = timezones.tz_standardize(own_state[2])
422410

423411
else: # pragma: no cover
424412
data = np.empty(state)
425413
np.ndarray.__setstate__(data, state)
414+
dtarr = DatetimeArray(data)
426415

427-
self._data = data
416+
self._eadata = dtarr
428417
self._reset_identity()
429418

430419
else:
@@ -502,7 +491,9 @@ def union(self, other):
502491
else:
503492
result = Index.union(this, other)
504493
if isinstance(result, DatetimeIndex):
505-
result._tz = timezones.tz_standardize(this.tz)
494+
# TODO: we shouldn't be setting attributes like this;
495+
# in all the tests this equality already holds
496+
result._eadata._dtype = this.dtype
506497
if (result.freq is None and
507498
(this.freq is not None or other.freq is not None)):
508499
result.freq = to_offset(result.inferred_freq)
@@ -530,11 +521,12 @@ def union_many(self, others):
530521
if this._can_fast_union(other):
531522
this = this._fast_union(other)
532523
else:
533-
tz = this.tz
524+
dtype = this.dtype
534525
this = Index.union(this, other)
535526
if isinstance(this, DatetimeIndex):
536-
this._tz = timezones.tz_standardize(tz)
537-
527+
# TODO: we shouldn't be setting attributes like this;
528+
# in all the tests this equality already holds
529+
this._eadata._dtype = dtype
538530
return this
539531

540532
def _can_fast_union(self, other):
@@ -1129,9 +1121,20 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None):
11291121
# Wrapping DatetimeArray
11301122

11311123
@property
1132-
def _eadata(self):
1133-
return DatetimeArray._simple_new(self._data,
1134-
tz=self.tz, freq=self.freq)
1124+
def _data(self):
1125+
return self._eadata._data
1126+
1127+
@property
1128+
def tz(self):
1129+
# GH#18595
1130+
return self._eadata.tz
1131+
1132+
@tz.setter
1133+
def tz(self, value):
1134+
# GH#3746; DatetimeArray will raise to disallow setting
1135+
self._eadata.tz = value
1136+
1137+
tzinfo = tz
11351138

11361139
# Compat for frequency inference, see GH#23789
11371140
_is_monotonic_increasing = Index.is_monotonic_increasing
@@ -1168,18 +1171,6 @@ def offset(self, value):
11681171
warnings.warn(msg, FutureWarning, stacklevel=2)
11691172
self.freq = value
11701173

1171-
@property
1172-
def freq(self):
1173-
return self._freq
1174-
1175-
@freq.setter
1176-
def freq(self, value):
1177-
if value is not None:
1178-
# let DatetimeArray to validation
1179-
self._eadata.freq = value
1180-
1181-
self._freq = to_offset(value)
1182-
11831174
def __getitem__(self, key):
11841175
result = self._eadata.__getitem__(key)
11851176
if is_scalar(result):

pandas/core/indexes/period.py

+1-34
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
from pandas._libs.tslibs import NaT, iNaT, resolution
99
from pandas._libs.tslibs.period import (
1010
DIFFERENT_FREQ, IncompatibleFrequency, Period)
11-
from pandas.util._decorators import (
12-
Appender, Substitution, cache_readonly, deprecate_kwarg)
11+
from pandas.util._decorators import Appender, Substitution, cache_readonly
1312

1413
from pandas.core.dtypes.common import (
1514
is_bool_dtype, is_datetime64_any_dtype, is_float, is_float_dtype,
@@ -288,10 +287,6 @@ def _simple_new(cls, values, name=None, freq=None, **kwargs):
288287
def _eadata(self):
289288
return self._data
290289

291-
@property
292-
def _ndarray_values(self):
293-
return self._data._ndarray_values
294-
295290
@property
296291
def values(self):
297292
return np.asarray(self)
@@ -472,34 +467,6 @@ def _int64index(self):
472467
# ------------------------------------------------------------------------
473468
# Index Methods
474469

475-
@deprecate_kwarg(old_arg_name='n', new_arg_name='periods')
476-
def shift(self, periods):
477-
"""
478-
Shift index by desired number of increments.
479-
480-
This method is for shifting the values of period indexes
481-
by a specified time increment.
482-
483-
Parameters
484-
----------
485-
periods : int, default 1
486-
Number of periods (or increments) to shift by,
487-
can be positive or negative.
488-
489-
.. versionchanged:: 0.24.0
490-
491-
Returns
492-
-------
493-
pandas.PeriodIndex
494-
Shifted index.
495-
496-
See Also
497-
--------
498-
DatetimeIndex.shift : Shift values of DatetimeIndex.
499-
"""
500-
i8values = self._data._time_shift(periods)
501-
return self._simple_new(i8values, name=self.name, freq=self.freq)
502-
503470
def _coerce_scalar_to_index(self, item):
504471
"""
505472
we need to coerce a scalar to a compat for our index type

pandas/core/indexes/timedeltas.py

+6-17
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,12 @@ def _simple_new(cls, values, name=None, freq=None, dtype=_TD_DTYPE):
235235
freq = to_offset(freq)
236236
tdarr = TimedeltaArray._simple_new(values, freq=freq)
237237
result = object.__new__(cls)
238-
result._data = tdarr._data
239-
result._freq = tdarr._freq
238+
result._eadata = tdarr
240239
result.name = name
241240
# For groupby perf. See note in indexes/base about _index_data
242-
result._index_data = result._data
241+
# TODO: make sure this is updated correctly if edited
242+
result._index_data = tdarr._data
243+
243244
result._reset_identity()
244245
return result
245246

@@ -279,8 +280,8 @@ def _format_native_types(self, na_rep='NaT', date_format=None, **kwargs):
279280
# Wrapping TimedeltaArray
280281

281282
@property
282-
def _eadata(self):
283-
return TimedeltaArray._simple_new(self._data, freq=self.freq)
283+
def _data(self):
284+
return self._eadata._data
284285

285286
__mul__ = _make_wrapped_arith_op("__mul__")
286287
__rmul__ = _make_wrapped_arith_op("__rmul__")
@@ -316,18 +317,6 @@ def __getitem__(self, key):
316317
return result
317318
return type(self)(result, name=self.name)
318319

319-
@property
320-
def freq(self): # TODO: get via eadata
321-
return self._freq
322-
323-
@freq.setter
324-
def freq(self, value): # TODO: get via eadata
325-
if value is not None:
326-
# dispatch to TimedeltaArray to validate frequency
327-
self._eadata.freq = value
328-
329-
self._freq = to_offset(value)
330-
331320
# -------------------------------------------------------------------
332321

333322
@Appender(_index_shared_docs['astype'])

pandas/tests/arithmetic/test_timedelta64.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,7 @@ def test_tdi_rmul_arraylike(self, other, box_with_array):
14761476

14771477
tdi = TimedeltaIndex(['1 Day'] * 10)
14781478
expected = timedelta_range('1 days', '10 days')
1479-
expected._freq = None
1479+
expected._eadata._freq = None
14801480

14811481
tdi = tm.box_expected(tdi, box)
14821482
expected = tm.box_expected(expected, xbox)

0 commit comments

Comments
 (0)