diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 8bcdd42b9f795..fc915f5f84d8b 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -425,7 +425,7 @@ def __contains__(self, item: object) -> bool | np.bool_: if not self._can_hold_na: return False elif item is self.dtype.na_value or isinstance(item, self.dtype.type): - return self.isna().any() + return self._hasnans else: return False else: @@ -603,6 +603,16 @@ def isna(self) -> np.ndarray | ExtensionArraySupportsAnyAll: """ raise AbstractMethodError(self) + @property + def _hasnans(self) -> bool: + # GH#22680 + """ + Equivalent to `self.isna().any()`. + + Some ExtensionArray subclasses may be able to optimize this check. + """ + return bool(self.isna().any()) + def _values_for_argsort(self) -> np.ndarray: """ Return values for sorting. @@ -686,7 +696,7 @@ def argmin(self, skipna: bool = True) -> int: ExtensionArray.argmax """ validate_bool_kwarg(skipna, "skipna") - if not skipna and self.isna().any(): + if not skipna and self._hasnans: raise NotImplementedError return nargminmax(self, "argmin") @@ -710,7 +720,7 @@ def argmax(self, skipna: bool = True) -> int: ExtensionArray.argmin """ validate_bool_kwarg(skipna, "skipna") - if not skipna and self.isna().any(): + if not skipna and self._hasnans: raise NotImplementedError return nargminmax(self, "argmax") diff --git a/pandas/core/dtypes/base.py b/pandas/core/dtypes/base.py index 372abee701b02..afd0d69e7e829 100644 --- a/pandas/core/dtypes/base.py +++ b/pandas/core/dtypes/base.py @@ -17,6 +17,7 @@ from pandas._libs.hashtable import object_hash from pandas._typing import ( DtypeObj, + Shape, npt, type_t, ) @@ -208,6 +209,19 @@ def construct_array_type(cls) -> type_t[ExtensionArray]: """ raise AbstractMethodError(cls) + def empty(self, shape: Shape) -> type_t[ExtensionArray]: + """ + Construct an ExtensionArray of this dtype with the given shape. + + Analogous to numpy.empty. + + Returns + ------- + ExtensionArray + """ + cls = self.construct_array_type() + return cls._empty(shape, dtype=self) + @classmethod def construct_from_string( cls: type_t[ExtensionDtypeT], string: str diff --git a/pandas/tests/extension/base/constructors.py b/pandas/tests/extension/base/constructors.py index 4ba315eeaeb15..c86054aed7e4d 100644 --- a/pandas/tests/extension/base/constructors.py +++ b/pandas/tests/extension/base/constructors.py @@ -126,6 +126,12 @@ def test_construct_empty_dataframe(self, dtype): def test_empty(self, dtype): cls = dtype.construct_array_type() result = cls._empty((4,), dtype=dtype) - assert isinstance(result, cls) assert result.dtype == dtype + assert result.shape == (4,) + + # GH#19600 method on ExtensionDtype + result2 = dtype.empty((4,)) + assert isinstance(result2, cls) + assert result2.dtype == dtype + assert result2.shape == (4,)