Skip to content

API: make construct_array_type non-classmethod #62060

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

Merged
merged 2 commits into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ Other API changes
an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining
the dtype of the resulting Index (:issue:`60797`)
- :class:`IncompatibleFrequency` now subclasses ``TypeError`` instead of ``ValueError``. As a result, joins with mismatched frequencies now cast to object like other non-comparable joins, and arithmetic with indexes with mismatched frequencies align (:issue:`55782`)
- :meth:`ExtensionDtype.construct_array_type` is now a regular method instead of a ``classmethod`` (:issue:`58663`)
- Comparison operations between :class:`Index` and :class:`Series` now consistently return :class:`Series` regardless of which object is on the left or right (:issue:`36759`)
- Numpy functions like ``np.isinf`` that return a bool dtype when called on a :class:`Index` object now return a bool-dtype :class:`Index` instead of ``np.ndarray`` (:issue:`52676`)

Expand Down
3 changes: 1 addition & 2 deletions pandas/core/arrays/boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ def kind(self) -> str:
def numpy_dtype(self) -> np.dtype:
return np.dtype("bool")

@classmethod
def construct_array_type(cls) -> type_t[BooleanArray]:
def construct_array_type(self) -> type_t[BooleanArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/core/arrays/floating.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ class FloatingDtype(NumericDtype):
_default_np_dtype = np.dtype(np.float64)
_checker = is_float_dtype

@classmethod
def construct_array_type(cls) -> type[FloatingArray]:
def construct_array_type(self) -> type[FloatingArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/core/arrays/integer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ class IntegerDtype(NumericDtype):
_default_np_dtype = np.dtype(np.int64)
_checker = is_integer_dtype

@classmethod
def construct_array_type(cls) -> type[IntegerArray]:
def construct_array_type(self) -> type[IntegerArray]:
"""
Return the array type associated with this dtype.

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/arrays/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def _coerce_to_data_and_mask(
if dtype is not None:
dtype = dtype_cls._standardize_dtype(dtype)

cls = dtype_cls.construct_array_type()
cls = dtype_cls().construct_array_type()
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Is it possible to change _coerce_to_data_and_mask to pass an dtype instance here?

Copy link
Member Author

Choose a reason for hiding this comment

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

thats slightly more invasive since it means updating the annotations etc

Copy link
Member

Choose a reason for hiding this comment

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

OK good for a follow up then. Would like to avoid always assuming here that it's safe to instantiate these types with no arguments

if isinstance(values, cls):
values, mask = values._data, values._mask
if dtype is not None:
Expand Down
7 changes: 1 addition & 6 deletions pandas/core/arrays/string_.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,7 @@ def construct_from_string(cls, string) -> Self:
else:
raise TypeError(f"Cannot construct a '{cls.__name__}' from '{string}'")

# https://github.com/pandas-dev/pandas/issues/36126
# error: Signature of "construct_array_type" incompatible with supertype
# "ExtensionDtype"
def construct_array_type( # type: ignore[override]
self,
) -> type_t[BaseStringArray]:
def construct_array_type(self) -> type_t[BaseStringArray]:
"""
Return the array type associated with this dtype.

Expand Down
5 changes: 2 additions & 3 deletions pandas/core/dtypes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,15 @@ def names(self) -> list[str] | None:
"""
return None

@classmethod
def construct_array_type(cls) -> type_t[ExtensionArray]:
def construct_array_type(self) -> type_t[ExtensionArray]:
"""
Return the array type associated with this dtype.

Returns
-------
type
"""
raise AbstractMethodError(cls)
raise AbstractMethodError(self)

def empty(self, shape: Shape) -> ExtensionArray:
"""
Expand Down
24 changes: 8 additions & 16 deletions pandas/core/dtypes/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,7 @@ def _hash_categories(self) -> int:
combined_hashed = combine_hash_arrays(iter(cat_array), num_items=len(cat_array))
return np.bitwise_xor.reduce(combined_hashed)

@classmethod
def construct_array_type(cls) -> type_t[Categorical]:
def construct_array_type(self) -> type_t[Categorical]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -856,8 +855,7 @@ def tz(self) -> tzinfo:
"""
return self._tz

@classmethod
def construct_array_type(cls) -> type_t[DatetimeArray]:
def construct_array_type(self) -> type_t[DatetimeArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -1174,8 +1172,7 @@ def is_dtype(cls, dtype: object) -> bool:
return False
return super().is_dtype(dtype)

@classmethod
def construct_array_type(cls) -> type_t[PeriodArray]:
def construct_array_type(self) -> type_t[PeriodArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -1363,8 +1360,7 @@ def subtype(self):
"""
return self._subtype

@classmethod
def construct_array_type(cls) -> type[IntervalArray]:
def construct_array_type(self) -> type[IntervalArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -1576,8 +1572,7 @@ def construct_from_string(cls, string: str) -> NumpyEADtype:
raise TypeError(msg) from err
return cls(dtype)

@classmethod
def construct_array_type(cls) -> type_t[NumpyExtensionArray]:
def construct_array_type(self) -> type_t[NumpyExtensionArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -1649,8 +1644,7 @@ def itemsize(self) -> int:
"""Return the number of bytes in this dtype"""
return self.numpy_dtype.itemsize

@classmethod
def construct_array_type(cls) -> type_t[BaseMaskedArray]:
def construct_array_type(self) -> type_t[BaseMaskedArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -1914,8 +1908,7 @@ def name(self) -> str:
def __repr__(self) -> str:
return self.name

@classmethod
def construct_array_type(cls) -> type_t[SparseArray]:
def construct_array_type(self) -> type_t[SparseArray]:
"""
Return the array type associated with this dtype.

Expand Down Expand Up @@ -2316,8 +2309,7 @@ def itemsize(self) -> int:
"""Return the number of bytes in this dtype"""
return self.numpy_dtype.itemsize

@classmethod
def construct_array_type(cls) -> type_t[ArrowExtensionArray]:
def construct_array_type(self) -> type_t[ArrowExtensionArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/arrays/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,7 @@ def test_bounds_check():
class DecimalDtype2(DecimalDtype):
name = "decimal2"

@classmethod
def construct_array_type(cls):
def construct_array_type(self):
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/dtypes/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,8 +576,7 @@ def type(self):
def name(self):
raise NotImplementedError

@classmethod
def construct_array_type(cls):
def construct_array_type(self):
raise NotImplementedError

def _is_numeric(self) -> bool:
Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/array_with_attr/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ class FloatAttrDtype(ExtensionDtype):
name = "float_attr"
na_value = np.nan

@classmethod
def construct_array_type(cls) -> type_t[FloatAttrArray]:
def construct_array_type(self) -> type_t[FloatAttrArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/date/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ def construct_from_string(cls, string: str):
else:
raise TypeError(f"Cannot construct a '{cls.__name__}' from '{string}'")

@classmethod
def construct_array_type(cls):
def construct_array_type(self):
return DateArray

@property
Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/decimal/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ def __init__(self, context=None) -> None:
def __repr__(self) -> str:
return f"DecimalDtype(context={self.context})"

@classmethod
def construct_array_type(cls) -> type_t[DecimalArray]:
def construct_array_type(self) -> type_t[DecimalArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/decimal/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,7 @@ def test_combine_from_sequence_raises(monkeypatch):
# https://github.com/pandas-dev/pandas/issues/22850
cls = DecimalArrayWithoutFromSequence

@classmethod
def construct_array_type(cls):
def construct_array_type(self):
return DecimalArrayWithoutFromSequence

monkeypatch.setattr(DecimalDtype, "construct_array_type", construct_array_type)
Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/json/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ class JSONDtype(ExtensionDtype):
name = "json"
na_value: Mapping[str, Any] = UserDict()

@classmethod
def construct_array_type(cls) -> type_t[JSONArray]:
def construct_array_type(self) -> type_t[JSONArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/extension/list/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class ListDtype(ExtensionDtype):
name = "list"
na_value = np.nan

@classmethod
def construct_array_type(cls) -> type_t[ListArray]:
def construct_array_type(self) -> type_t[ListArray]:
"""
Return the array type associated with this dtype.

Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/frame/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -857,8 +857,7 @@ def copy(self):
class Int16DtypeNoCopy(pd.Int16Dtype):
# GH 42501

@classmethod
def construct_array_type(cls):
def construct_array_type(self):
return IntegerArrayNoCopy


Expand Down
Loading