Skip to content

Commit 6993c1b

Browse files
jbrockmendeljreback
authored andcommitted
Make pd.Period immutable (#17239)
1 parent d45e12b commit 6993c1b

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

doc/source/whatsnew/v0.21.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ Other API Changes
291291
- Moved definition of ``MergeError`` to the ``pandas.errors`` module.
292292
- The signature of :func:`Series.set_axis` and :func:`DataFrame.set_axis` has been changed from ``set_axis(axis, labels)`` to ``set_axis(labels, axis=0)``, for consistency with the rest of the API. The old signature is deprecated and will show a ``FutureWarning`` (:issue:`14636`)
293293
- :func:`Series.argmin` and :func:`Series.argmax` will now raise a ``TypeError`` when used with ``object`` dtypes, instead of a ``ValueError`` (:issue:`13595`)
294+
- :class:`Period` is now immutable, and will now raise an ``AttributeError`` when a user tries to assign a new value to the ``ordinal`` or ``freq`` attributes (:issue:`17116`).
295+
294296

295297
.. _whatsnew_0210.deprecations:
296298

pandas/_libs/period.pyx

+11-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ from datetime cimport (
2929
PANDAS_FR_ns,
3030
INT32_MIN)
3131

32+
3233
cimport util, lib
34+
3335
from lib cimport is_null_datetimelike, is_period
3436
from pandas._libs import tslib, lib
3537
from pandas._libs.tslib import (Timedelta, Timestamp, iNaT,
@@ -668,13 +670,17 @@ class IncompatibleFrequency(ValueError):
668670

669671
cdef class _Period(object):
670672

671-
cdef public:
673+
cdef readonly:
672674
int64_t ordinal
673675
object freq
674676

675677
_comparables = ['name', 'freqstr']
676678
_typ = 'period'
677679

680+
def __cinit__(self, ordinal, freq):
681+
self.ordinal = ordinal
682+
self.freq = freq
683+
678684
@classmethod
679685
def _maybe_convert_freq(cls, object freq):
680686

@@ -698,9 +704,8 @@ cdef class _Period(object):
698704
if ordinal == iNaT:
699705
return NaT
700706
else:
701-
self = _Period.__new__(cls)
702-
self.ordinal = ordinal
703-
self.freq = cls._maybe_convert_freq(freq)
707+
freq = cls._maybe_convert_freq(freq)
708+
self = _Period.__new__(cls, ordinal, freq)
704709
return self
705710

706711
def __richcmp__(self, other, op):
@@ -752,7 +757,7 @@ cdef class _Period(object):
752757
def __add__(self, other):
753758
if isinstance(self, Period):
754759
if isinstance(other, (timedelta, np.timedelta64,
755-
offsets.Tick, offsets.DateOffset,
760+
offsets.DateOffset,
756761
Timedelta)):
757762
return self._add_delta(other)
758763
elif other is NaT:
@@ -770,7 +775,7 @@ cdef class _Period(object):
770775
def __sub__(self, other):
771776
if isinstance(self, Period):
772777
if isinstance(other, (timedelta, np.timedelta64,
773-
offsets.Tick, offsets.DateOffset,
778+
offsets.DateOffset,
774779
Timedelta)):
775780
neg_other = -other
776781
return self + neg_other

pandas/tests/scalar/test_period.py

+11
Original file line numberDiff line numberDiff line change
@@ -1406,3 +1406,14 @@ def test_period_ops_offset(self):
14061406

14071407
with tm.assert_raises_regex(period.IncompatibleFrequency, msg):
14081408
p - offsets.Hour(2)
1409+
1410+
1411+
def test_period_immutable():
1412+
# see gh-17116
1413+
per = pd.Period('2014Q1')
1414+
with pytest.raises(AttributeError):
1415+
per.ordinal = 14
1416+
1417+
freq = per.freq
1418+
with pytest.raises(AttributeError):
1419+
per.freq = 2 * freq

0 commit comments

Comments
 (0)