Skip to content

Commit 187ebcf

Browse files
committed
Remove NDFrame.__constructor_expanddim
1 parent f273385 commit 187ebcf

File tree

6 files changed

+15
-35
lines changed

6 files changed

+15
-35
lines changed

doc/source/development/extending.rst

+4-14
Original file line numberDiff line numberDiff line change
@@ -329,21 +329,11 @@ Each data structure has several *constructor properties* for returning a new
329329
data structure as the result of an operation. By overriding these properties,
330330
you can retain subclasses through ``pandas`` data manipulations.
331331

332-
There are 3 constructor properties to be defined:
332+
There are 3 possible constructor properties to be defined on a subclass:
333333

334-
* ``_constructor``: Used when a manipulation result has the same dimensions as the original.
335-
* ``_constructor_sliced``: Used when a manipulation result has one lower dimension(s) as the original, such as ``DataFrame`` single columns slicing.
336-
* ``_constructor_expanddim``: Used when a manipulation result has one higher dimension as the original, such as ``Series.to_frame()``.
337-
338-
Following table shows how ``pandas`` data structures define constructor properties by default.
339-
340-
=========================== ======================= =============
341-
Property Attributes ``Series`` ``DataFrame``
342-
=========================== ======================= =============
343-
``_constructor`` ``Series`` ``DataFrame``
344-
``_constructor_sliced`` ``NotImplementedError`` ``Series``
345-
``_constructor_expanddim`` ``DataFrame`` ``NotImplementedError``
346-
=========================== ======================= =============
334+
* ``DataFrame/Series._constructor``: Used when a manipulation result has the same (sub-)class as the original.
335+
* ``DataFrame._constructor_sliced``: Used when a ``DataFrame`` (sub-)class manipulation result should be a ``Series`` (sub-)class.
336+
* ``Series._constructor_expanddim``: Used when a ``Series`` (sub-)class manipulation result should be a ``DataFrame`` (sub-)class, e.g. ``Series.to_frame()``.
347337

348338
Below example shows how to define ``SubclassedSeries`` and ``SubclassedDataFrame`` overriding constructor properties.
349339

doc/source/whatsnew/v1.3.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ See :ref:`install.dependencies` and :ref:`install.optional_dependencies` for mor
216216
Other API changes
217217
^^^^^^^^^^^^^^^^^
218218
- Partially initialized :class:`CategoricalDtype` (i.e. those with ``categories=None`` objects will no longer compare as equal to fully initialized dtype objects.
219-
-
219+
- Accessing ``_constructor_expanddim`` on a :class:`DataFrame` and ``_constructor_sliced`` on a :class:`Series` now raise an ``AttributeError``. Previously a ``NotImplementedError`` was raised (:issue:`38782`)
220220
-
221221

222222
.. ---------------------------------------------------------------------------

pandas/core/frame.py

+1-9
Original file line numberDiff line numberDiff line change
@@ -490,20 +490,12 @@ class DataFrame(NDFrame, OpsMixin):
490490
_HANDLED_TYPES = (Series, Index, ExtensionArray, np.ndarray)
491491
_accessors: Set[str] = {"sparse"}
492492
_hidden_attrs: FrozenSet[str] = NDFrame._hidden_attrs | frozenset([])
493-
_constructor_sliced: Type[Series] = Series
494493

495494
@property
496495
def _constructor(self) -> Type[DataFrame]:
497496
return DataFrame
498497

499-
@property
500-
def _constructor_expanddim(self):
501-
# GH#31549 raising NotImplementedError on a property causes trouble
502-
# for `inspect`
503-
def constructor(*args, **kwargs):
504-
raise NotImplementedError("Not supported for DataFrames!")
505-
506-
return constructor
498+
_constructor_sliced: Type[Series] = Series
507499

508500
# ----------------------------------------------------------------------
509501
# Constructors

pandas/core/generic.py

-8
Original file line numberDiff line numberDiff line change
@@ -375,14 +375,6 @@ def _constructor(self: FrameOrSeries) -> Type[FrameOrSeries]:
375375
"""
376376
raise AbstractMethodError(self)
377377

378-
@property
379-
def _constructor_expanddim(self):
380-
"""
381-
Used when a manipulation result has one higher dimension as the
382-
original, such as Series.to_frame()
383-
"""
384-
raise NotImplementedError
385-
386378
# ----------------------------------------------------------------------
387379
# Internals
388380

pandas/core/series.py

+4
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,10 @@ def _constructor(self) -> Type[Series]:
403403

404404
@property
405405
def _constructor_expanddim(self) -> Type[DataFrame]:
406+
"""
407+
Used when a manipulation result has one higher dimension as the
408+
original, such as Series.to_frame()
409+
"""
406410
from pandas.core.frame import DataFrame
407411

408412
return DataFrame

pandas/tests/frame/test_api.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,13 @@ def test_set_flags(self, allows_duplicate_labels, frame_or_series):
330330
assert obj.iloc[key] == 0
331331

332332
def test_constructor_expanddim_lookup(self):
333-
# GH#33628 accessing _constructor_expanddim should not
334-
# raise NotImplementedError
333+
# GH#33628 accessing _constructor_expanddim should not raise NotImplementedError
334+
# GH38782 pandas has no container higher than DatafFame (two-dim), so
335+
# DataFrame._constructor_expand_dim, doesn't make sense, so is removed.
335336
df = DataFrame()
336337

337-
with pytest.raises(NotImplementedError, match="Not supported for DataFrames!"):
338+
msg = "'DataFrame' object has no attribute '_constructor_expanddim'"
339+
with pytest.raises(AttributeError, match=msg):
338340
df._constructor_expanddim(np.arange(27).reshape(3, 3, 3))
339341

340342
@skip_if_no("jinja2")

0 commit comments

Comments
 (0)