diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index 3bcc3159ddf58..c0cca1852b446 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -6,7 +6,6 @@ Any, Literal, Sequence, - TypeVar, cast, overload, ) @@ -23,11 +22,11 @@ PositionalIndexer2D, PositionalIndexerTuple, ScalarIndexer, + Self, SequenceIndexer, Shape, TakeIndexer, npt, - type_t, ) from pandas.errors import AbstractMethodError from pandas.util._decorators import doc @@ -61,10 +60,6 @@ from pandas.core.indexers import check_array_indexer from pandas.core.sorting import nargminmax -NDArrayBackedExtensionArrayT = TypeVar( - "NDArrayBackedExtensionArrayT", bound="NDArrayBackedExtensionArray" -) - if TYPE_CHECKING: from pandas._typing import ( NumpySorter, @@ -153,13 +148,13 @@ def view(self, dtype: Dtype | None = None) -> ArrayLike: return arr.view(dtype=dtype) # type: ignore[arg-type] def take( - self: NDArrayBackedExtensionArrayT, + self, indices: TakeIndexer, *, allow_fill: bool = False, fill_value: Any = None, axis: AxisInt = 0, - ) -> NDArrayBackedExtensionArrayT: + ) -> Self: if allow_fill: fill_value = self._validate_scalar(fill_value) @@ -218,17 +213,17 @@ def argmax(self, axis: AxisInt = 0, skipna: bool = True): # type: ignore[overri raise NotImplementedError return nargminmax(self, "argmax", axis=axis) - def unique(self: NDArrayBackedExtensionArrayT) -> NDArrayBackedExtensionArrayT: + def unique(self) -> Self: new_data = unique(self._ndarray) return self._from_backing_data(new_data) @classmethod @doc(ExtensionArray._concat_same_type) def _concat_same_type( - cls: type[NDArrayBackedExtensionArrayT], - to_concat: Sequence[NDArrayBackedExtensionArrayT], + cls, + to_concat: Sequence[Self], axis: AxisInt = 0, - ) -> NDArrayBackedExtensionArrayT: + ) -> Self: dtypes = {str(x.dtype) for x in to_concat} if len(dtypes) != 1: raise ValueError("to_concat must have the same dtype (tz)", dtypes) @@ -268,15 +263,15 @@ def __getitem__(self, key: ScalarIndexer) -> Any: @overload def __getitem__( - self: NDArrayBackedExtensionArrayT, + self, key: SequenceIndexer | PositionalIndexerTuple, - ) -> NDArrayBackedExtensionArrayT: + ) -> Self: ... def __getitem__( - self: NDArrayBackedExtensionArrayT, + self, key: PositionalIndexer2D, - ) -> NDArrayBackedExtensionArrayT | Any: + ) -> Self | Any: if lib.is_integer(key): # fast-path result = self._ndarray[key] @@ -303,9 +298,7 @@ def _fill_mask_inplace( func(self._ndarray.T, limit=limit, mask=mask.T) @doc(ExtensionArray.fillna) - def fillna( - self: NDArrayBackedExtensionArrayT, value=None, method=None, limit=None - ) -> NDArrayBackedExtensionArrayT: + def fillna(self, value=None, method=None, limit=None) -> Self: value, method = validate_fillna_kwargs( value, method, validate_scalar_dict_value=False ) @@ -369,9 +362,7 @@ def _putmask(self, mask: npt.NDArray[np.bool_], value) -> None: np.putmask(self._ndarray, mask, value) - def _where( - self: NDArrayBackedExtensionArrayT, mask: npt.NDArray[np.bool_], value - ) -> NDArrayBackedExtensionArrayT: + def _where(self: Self, mask: npt.NDArray[np.bool_], value) -> Self: """ Analogue to np.where(mask, self, value) @@ -393,9 +384,7 @@ def _where( # ------------------------------------------------------------------------ # Index compat methods - def insert( - self: NDArrayBackedExtensionArrayT, loc: int, item - ) -> NDArrayBackedExtensionArrayT: + def insert(self, loc: int, item) -> Self: """ Make new ExtensionArray inserting new item at location. Follows Python list.append semantics for negative values. @@ -461,10 +450,10 @@ def value_counts(self, dropna: bool = True) -> Series: return Series(result._values, index=index, name=result.name) def _quantile( - self: NDArrayBackedExtensionArrayT, + self, qs: npt.NDArray[np.float64], interpolation: str, - ) -> NDArrayBackedExtensionArrayT: + ) -> Self: # TODO: disable for Categorical if not ordered? mask = np.asarray(self.isna()) @@ -488,9 +477,7 @@ def _cast_quantile_result(self, res_values: np.ndarray) -> np.ndarray: # numpy-like methods @classmethod - def _empty( - cls: type_t[NDArrayBackedExtensionArrayT], shape: Shape, dtype: ExtensionDtype - ) -> NDArrayBackedExtensionArrayT: + def _empty(cls, shape: Shape, dtype: ExtensionDtype) -> Self: """ Analogous to np.empty(shape, dtype=dtype) diff --git a/pandas/core/arrays/arrow/array.py b/pandas/core/arrays/arrow/array.py index 2eb723ecb8e37..c07aee737934b 100644 --- a/pandas/core/arrays/arrow/array.py +++ b/pandas/core/arrays/arrow/array.py @@ -8,7 +8,6 @@ Callable, Literal, Sequence, - TypeVar, cast, ) @@ -24,6 +23,7 @@ NpDtype, PositionalIndexer, Scalar, + Self, SortKind, TakeIndexer, TimeAmbiguous, @@ -140,8 +140,6 @@ def floordiv_compat( from pandas import Series -ArrowExtensionArrayT = TypeVar("ArrowExtensionArrayT", bound="ArrowExtensionArray") - def get_unit_from_pa_dtype(pa_dtype): # https://github.com/pandas-dev/pandas/pull/50998#discussion_r1100344804 @@ -419,16 +417,16 @@ def __array__(self, dtype: NpDtype | None = None) -> np.ndarray: """Correctly construct numpy arrays when passed to `np.asarray()`.""" return self.to_numpy(dtype=dtype) - def __invert__(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def __invert__(self) -> Self: return type(self)(pc.invert(self._pa_array)) - def __neg__(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def __neg__(self) -> Self: return type(self)(pc.negate_checked(self._pa_array)) - def __pos__(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def __pos__(self) -> Self: return type(self)(self._pa_array) - def __abs__(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def __abs__(self) -> Self: return type(self)(pc.abs_checked(self._pa_array)) # GH 42600: __getstate__/__setstate__ not necessary once @@ -733,7 +731,7 @@ def argmin(self, skipna: bool = True) -> int: def argmax(self, skipna: bool = True) -> int: return self._argmin_max(skipna, "max") - def copy(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def copy(self) -> Self: """ Return a shallow copy of the array. @@ -745,7 +743,7 @@ def copy(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: """ return type(self)(self._pa_array) - def dropna(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def dropna(self) -> Self: """ Return ArrowExtensionArray without NA values. @@ -757,11 +755,11 @@ def dropna(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: @doc(ExtensionArray.fillna) def fillna( - self: ArrowExtensionArrayT, + self, value: object | ArrayLike | None = None, method: FillnaOptions | None = None, limit: int | None = None, - ) -> ArrowExtensionArrayT: + ) -> Self: value, method = validate_fillna_kwargs(value, method) if limit is not None: @@ -877,9 +875,7 @@ def reshape(self, *args, **kwargs): f"as backed by a 1D pyarrow.ChunkedArray." ) - def round( - self: ArrowExtensionArrayT, decimals: int = 0, *args, **kwargs - ) -> ArrowExtensionArrayT: + def round(self, decimals: int = 0, *args, **kwargs) -> Self: """ Round each value in the array a to the given number of decimals. @@ -1052,7 +1048,7 @@ def to_numpy( result[self.isna()] = na_value return result - def unique(self: ArrowExtensionArrayT) -> ArrowExtensionArrayT: + def unique(self) -> Self: """ Compute the ArrowExtensionArray of unique values. @@ -1123,9 +1119,7 @@ def value_counts(self, dropna: bool = True) -> Series: return Series(counts, index=index, name="count") @classmethod - def _concat_same_type( - cls: type[ArrowExtensionArrayT], to_concat - ) -> ArrowExtensionArrayT: + def _concat_same_type(cls, to_concat) -> Self: """ Concatenate multiple ArrowExtensionArrays. @@ -1456,9 +1450,7 @@ def _rank( return type(self)(result) - def _quantile( - self: ArrowExtensionArrayT, qs: npt.NDArray[np.float64], interpolation: str - ) -> ArrowExtensionArrayT: + def _quantile(self, qs: npt.NDArray[np.float64], interpolation: str) -> Self: """ Compute the quantiles of self for each quantile in `qs`. @@ -1495,7 +1487,7 @@ def _quantile( return type(self)(result) - def _mode(self: ArrowExtensionArrayT, dropna: bool = True) -> ArrowExtensionArrayT: + def _mode(self, dropna: bool = True) -> Self: """ Returns the mode(s) of the ExtensionArray. diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index bbfe9b9bbb6c7..9fd668405684f 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -17,7 +17,6 @@ Iterator, Literal, Sequence, - TypeVar, cast, overload, ) @@ -86,6 +85,7 @@ NumpyValueArrayLike, PositionalIndexer, ScalarIndexer, + Self, SequenceIndexer, Shape, SortKind, @@ -95,8 +95,6 @@ _extension_array_shared_docs: dict[str, str] = {} -ExtensionArrayT = TypeVar("ExtensionArrayT", bound="ExtensionArray") - class ExtensionArray: """ @@ -312,12 +310,10 @@ def __getitem__(self, item: ScalarIndexer) -> Any: ... @overload - def __getitem__(self: ExtensionArrayT, item: SequenceIndexer) -> ExtensionArrayT: + def __getitem__(self, item: SequenceIndexer) -> Self: ... - def __getitem__( - self: ExtensionArrayT, item: PositionalIndexer - ) -> ExtensionArrayT | Any: + def __getitem__(self, item: PositionalIndexer) -> Self | Any: """ Select a subset of self. @@ -755,11 +751,11 @@ def argmax(self, skipna: bool = True) -> int: return nargminmax(self, "argmax") def fillna( - self: ExtensionArrayT, + self, value: object | ArrayLike | None = None, method: FillnaOptions | None = None, limit: int | None = None, - ) -> ExtensionArrayT: + ) -> Self: """ Fill NA/NaN values using the specified method. @@ -811,7 +807,7 @@ def fillna( new_values = self.copy() return new_values - def dropna(self: ExtensionArrayT) -> ExtensionArrayT: + def dropna(self) -> Self: """ Return ExtensionArray without NA values. @@ -872,7 +868,7 @@ def shift(self, periods: int = 1, fill_value: object = None) -> ExtensionArray: b = empty return self._concat_same_type([a, b]) - def unique(self: ExtensionArrayT) -> ExtensionArrayT: + def unique(self) -> Self: """ Compute the ExtensionArray of unique values. @@ -1107,9 +1103,7 @@ def factorize( @Substitution(klass="ExtensionArray") @Appender(_extension_array_shared_docs["repeat"]) - def repeat( - self: ExtensionArrayT, repeats: int | Sequence[int], axis: AxisInt | None = None - ) -> ExtensionArrayT: + def repeat(self, repeats: int | Sequence[int], axis: AxisInt | None = None) -> Self: nv.validate_repeat((), {"axis": axis}) ind = np.arange(len(self)).repeat(repeats) return self.take(ind) @@ -1119,12 +1113,12 @@ def repeat( # ------------------------------------------------------------------------ def take( - self: ExtensionArrayT, + self, indices: TakeIndexer, *, allow_fill: bool = False, fill_value: Any = None, - ) -> ExtensionArrayT: + ) -> Self: """ Take elements from an array. @@ -1213,7 +1207,7 @@ def take(self, indices, allow_fill=False, fill_value=None): # pandas.api.extensions.take raise AbstractMethodError(self) - def copy(self: ExtensionArrayT) -> ExtensionArrayT: + def copy(self) -> Self: """ Return a copy of the array. @@ -1345,9 +1339,7 @@ def ravel(self, order: Literal["C", "F", "A", "K"] | None = "C") -> ExtensionArr return self @classmethod - def _concat_same_type( - cls: type[ExtensionArrayT], to_concat: Sequence[ExtensionArrayT] - ) -> ExtensionArrayT: + def _concat_same_type(cls, to_concat: Sequence[Self]) -> Self: """ Concatenate multiple array of this dtype. @@ -1489,11 +1481,11 @@ def tolist(self) -> list: return [x.tolist() for x in self] return list(self) - def delete(self: ExtensionArrayT, loc: PositionalIndexer) -> ExtensionArrayT: + def delete(self, loc: PositionalIndexer) -> Self: indexer = np.delete(np.arange(len(self)), loc) return self.take(indexer) - def insert(self: ExtensionArrayT, loc: int, item) -> ExtensionArrayT: + def insert(self, loc: int, item) -> Self: """ Insert an item at the given position. @@ -1548,9 +1540,7 @@ def _putmask(self, mask: npt.NDArray[np.bool_], value) -> None: self[mask] = val - def _where( - self: ExtensionArrayT, mask: npt.NDArray[np.bool_], value - ) -> ExtensionArrayT: + def _where(self, mask: npt.NDArray[np.bool_], value) -> Self: """ Analogue to np.where(mask, self, value) @@ -1638,9 +1628,7 @@ def _empty(cls, shape: Shape, dtype: ExtensionDtype): ) return result - def _quantile( - self: ExtensionArrayT, qs: npt.NDArray[np.float64], interpolation: str - ) -> ExtensionArrayT: + def _quantile(self, qs: npt.NDArray[np.float64], interpolation: str) -> Self: """ Compute the quantiles of self for each quantile in `qs`. @@ -1660,7 +1648,7 @@ def _quantile( res_values = quantile_with_mask(arr, mask, fill_value, qs, interpolation) return type(self)._from_sequence(res_values) - def _mode(self: ExtensionArrayT, dropna: bool = True) -> ExtensionArrayT: + def _mode(self, dropna: bool = True) -> Self: """ Returns the mode(s) of the ExtensionArray. @@ -1677,7 +1665,7 @@ def _mode(self: ExtensionArrayT, dropna: bool = True) -> ExtensionArrayT: Sorted, if possible. """ # error: Incompatible return value type (got "Union[ExtensionArray, - # ndarray[Any, Any]]", expected "ExtensionArrayT") + # ndarray[Any, Any]]", expected "Self") return mode(self, dropna=dropna) # type: ignore[return-value] def __array_ufunc__(self, ufunc: np.ufunc, method: str, *inputs, **kwargs): diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index b075fd53480d3..9ecba12d26beb 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -10,7 +10,6 @@ Iterator, Literal, Sequence, - TypeVar, cast, overload, ) @@ -104,6 +103,7 @@ Dtype, NpDtype, Ordered, + Self, Shape, SortKind, npt, @@ -117,9 +117,6 @@ ) -CategoricalT = TypeVar("CategoricalT", bound="Categorical") - - def _cat_compare_op(op): opname = f"__{op.__name__}__" fill_value = op is operator.ne @@ -2163,9 +2160,7 @@ def equals(self, other: object) -> bool: return False @classmethod - def _concat_same_type( - cls: type[CategoricalT], to_concat: Sequence[CategoricalT], axis: AxisInt = 0 - ) -> CategoricalT: + def _concat_same_type(cls, to_concat: Sequence[Self], axis: AxisInt = 0) -> Self: from pandas.core.dtypes.concat import union_categoricals first = to_concat[0] diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index a0008ad0015ed..657e045d60c2f 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -13,7 +13,6 @@ Iterator, Literal, Sequence, - TypeVar, Union, cast, final, @@ -63,6 +62,7 @@ PositionalIndexer2D, PositionalIndexerTuple, ScalarIndexer, + Self, SequenceIndexer, TimeAmbiguous, TimeNonexistent, @@ -155,7 +155,6 @@ ) DTScalarOrNaT = Union[DatetimeLikeScalar, NaTType] -DatetimeLikeArrayT = TypeVar("DatetimeLikeArrayT", bound="DatetimeLikeArrayMixin") def _period_dispatch(meth: F) -> F: @@ -351,14 +350,12 @@ def __getitem__(self, item: ScalarIndexer) -> DTScalarOrNaT: @overload def __getitem__( - self: DatetimeLikeArrayT, + self, item: SequenceIndexer | PositionalIndexerTuple, - ) -> DatetimeLikeArrayT: + ) -> Self: ... - def __getitem__( - self: DatetimeLikeArrayT, key: PositionalIndexer2D - ) -> DatetimeLikeArrayT | DTScalarOrNaT: + def __getitem__(self, key: PositionalIndexer2D) -> Self | DTScalarOrNaT: """ This getitem defers to the underlying array, which by-definition can only handle list-likes, slices, and integer scalars @@ -366,14 +363,12 @@ def __getitem__( # Use cast as we know we will get back a DatetimeLikeArray or DTScalar, # but skip evaluating the Union at runtime for performance # (see https://github.com/pandas-dev/pandas/pull/44624) - result = cast( - "Union[DatetimeLikeArrayT, DTScalarOrNaT]", super().__getitem__(key) - ) + result = cast("Union[Self, DTScalarOrNaT]", super().__getitem__(key)) if lib.is_scalar(result): return result else: # At this point we know the result is an array. - result = cast(DatetimeLikeArrayT, result) + result = cast(Self, result) result._freq = self._get_getitem_freq(key) return result @@ -489,7 +484,7 @@ def astype(self, dtype, copy: bool = True): return np.asarray(self, dtype=dtype) @overload - def view(self: DatetimeLikeArrayT) -> DatetimeLikeArrayT: + def view(self) -> Self: ... @overload @@ -515,10 +510,10 @@ def view(self, dtype: Dtype | None = None) -> ArrayLike: @classmethod def _concat_same_type( - cls: type[DatetimeLikeArrayT], - to_concat: Sequence[DatetimeLikeArrayT], + cls, + to_concat: Sequence[Self], axis: AxisInt = 0, - ) -> DatetimeLikeArrayT: + ) -> Self: new_obj = super()._concat_same_type(to_concat, axis) obj = to_concat[0] @@ -540,7 +535,7 @@ def _concat_same_type( new_obj._freq = new_freq return new_obj - def copy(self: DatetimeLikeArrayT, order: str = "C") -> DatetimeLikeArrayT: + def copy(self, order: str = "C") -> Self: # error: Unexpected keyword argument "order" for "copy" new_obj = super().copy(order=order) # type: ignore[call-arg] new_obj._freq = self.freq @@ -1453,7 +1448,7 @@ def __rsub__(self, other): # We get here with e.g. datetime objects return -(self - other) - def __iadd__(self: DatetimeLikeArrayT, other) -> DatetimeLikeArrayT: + def __iadd__(self, other) -> Self: result = self + other self[:] = result[:] @@ -1462,7 +1457,7 @@ def __iadd__(self: DatetimeLikeArrayT, other) -> DatetimeLikeArrayT: self._freq = result.freq return self - def __isub__(self: DatetimeLikeArrayT, other) -> DatetimeLikeArrayT: + def __isub__(self, other) -> Self: result = self - other self[:] = result[:] @@ -1476,10 +1471,10 @@ def __isub__(self: DatetimeLikeArrayT, other) -> DatetimeLikeArrayT: @_period_dispatch def _quantile( - self: DatetimeLikeArrayT, + self, qs: npt.NDArray[np.float64], interpolation: str, - ) -> DatetimeLikeArrayT: + ) -> Self: return super()._quantile(qs=qs, interpolation=interpolation) @_period_dispatch @@ -1779,9 +1774,6 @@ def strftime(self, date_format: str) -> npt.NDArray[np.object_]: """ -TimelikeOpsT = TypeVar("TimelikeOpsT", bound="TimelikeOps") - - class TimelikeOps(DatetimeLikeArrayMixin): """ Common ops for TimedeltaIndex/DatetimeIndex, but not PeriodIndex. @@ -1928,9 +1920,7 @@ def _validate_frequency(cls, index, freq, **kwargs): ) from err @classmethod - def _generate_range( - cls: type[DatetimeLikeArrayT], start, end, periods, freq, *args, **kwargs - ) -> DatetimeLikeArrayT: + def _generate_range(cls, start, end, periods, freq, *args, **kwargs) -> Self: raise AbstractMethodError(cls) # -------------------------------------------------------------- @@ -1946,7 +1936,7 @@ def unit(self) -> str: # "ExtensionDtype"; expected "Union[DatetimeTZDtype, dtype[Any]]" return dtype_to_unit(self.dtype) # type: ignore[arg-type] - def as_unit(self: TimelikeOpsT, unit: str) -> TimelikeOpsT: + def as_unit(self, unit: str) -> Self: if unit not in ["s", "ms", "us", "ns"]: raise ValueError("Supported units are 's', 'ms', 'us', 'ns'") diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 2d63af5dc8624..abc5606798cd9 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -11,7 +11,6 @@ Iterator, Literal, Sequence, - TypeVar, Union, cast, overload, @@ -37,6 +36,7 @@ NpDtype, PositionalIndexer, ScalarIndexer, + Self, SequenceIndexer, SortKind, TimeArrayLike, @@ -107,7 +107,6 @@ ) -IntervalArrayT = TypeVar("IntervalArrayT", bound="IntervalArray") IntervalSideT = Union[TimeArrayLike, np.ndarray] IntervalOrNA = Union[Interval, float] @@ -228,7 +227,7 @@ def ndim(self) -> Literal[1]: # Constructors def __new__( - cls: type[IntervalArrayT], + cls, data, closed=None, dtype: Dtype | None = None, @@ -280,11 +279,11 @@ def __new__( @classmethod def _simple_new( - cls: type[IntervalArrayT], + cls, left: IntervalSideT, right: IntervalSideT, dtype: IntervalDtype, - ) -> IntervalArrayT: + ) -> Self: result = IntervalMixin.__new__(cls) result._left = left result._right = right @@ -380,18 +379,16 @@ def _ensure_simple_new_inputs( @classmethod def _from_sequence( - cls: type[IntervalArrayT], + cls, scalars, *, dtype: Dtype | None = None, copy: bool = False, - ) -> IntervalArrayT: + ) -> Self: return cls(scalars, dtype=dtype, copy=copy) @classmethod - def _from_factorized( - cls: type[IntervalArrayT], values: np.ndarray, original: IntervalArrayT - ) -> IntervalArrayT: + def _from_factorized(cls, values: np.ndarray, original: IntervalArray) -> Self: if len(values) == 0: # An empty array returns object-dtype here. We can't create # a new IA from an (empty) object-dtype array, so turn it into the @@ -449,12 +446,12 @@ def _from_factorized( } ) def from_breaks( - cls: type[IntervalArrayT], + cls, breaks, closed: IntervalClosedType | None = "right", copy: bool = False, dtype: Dtype | None = None, - ) -> IntervalArrayT: + ) -> Self: breaks = _maybe_convert_platform_interval(breaks) return cls.from_arrays(breaks[:-1], breaks[1:], closed, copy=copy, dtype=dtype) @@ -526,13 +523,13 @@ def from_breaks( } ) def from_arrays( - cls: type[IntervalArrayT], + cls, left, right, closed: IntervalClosedType | None = "right", copy: bool = False, dtype: Dtype | None = None, - ) -> IntervalArrayT: + ) -> Self: left = _maybe_convert_platform_interval(left) right = _maybe_convert_platform_interval(right) @@ -599,12 +596,12 @@ def from_arrays( } ) def from_tuples( - cls: type[IntervalArrayT], + cls, data, closed: IntervalClosedType | None = "right", copy: bool = False, dtype: Dtype | None = None, - ) -> IntervalArrayT: + ) -> Self: if len(data): left, right = [], [] else: @@ -660,7 +657,7 @@ def _validate(cls, left, right, dtype: IntervalDtype) -> None: msg = "left side of interval must be <= right side" raise ValueError(msg) - def _shallow_copy(self: IntervalArrayT, left, right) -> IntervalArrayT: + def _shallow_copy(self, left, right) -> Self: """ Return a new IntervalArray with the replacement attributes @@ -706,12 +703,10 @@ def __getitem__(self, key: ScalarIndexer) -> IntervalOrNA: ... @overload - def __getitem__(self: IntervalArrayT, key: SequenceIndexer) -> IntervalArrayT: + def __getitem__(self, key: SequenceIndexer) -> Self: ... - def __getitem__( - self: IntervalArrayT, key: PositionalIndexer - ) -> IntervalArrayT | IntervalOrNA: + def __getitem__(self, key: PositionalIndexer) -> Self | IntervalOrNA: key = check_array_indexer(self, key) left = self._left[key] right = self._right[key] @@ -894,9 +889,7 @@ def max(self, *, axis: AxisInt | None = None, skipna: bool = True) -> IntervalOr indexer = obj.argsort()[-1] return obj[indexer] - def fillna( - self: IntervalArrayT, value=None, method=None, limit=None - ) -> IntervalArrayT: + def fillna(self, value=None, method=None, limit=None) -> Self: """ Fill NA/NaN values using the specified method. @@ -1006,9 +999,7 @@ def equals(self, other) -> bool: ) @classmethod - def _concat_same_type( - cls: type[IntervalArrayT], to_concat: Sequence[IntervalArrayT] - ) -> IntervalArrayT: + def _concat_same_type(cls, to_concat: Sequence[IntervalArray]) -> Self: """ Concatenate multiple IntervalArray @@ -1032,7 +1023,7 @@ def _concat_same_type( return cls._simple_new(left, right, dtype=dtype) - def copy(self: IntervalArrayT) -> IntervalArrayT: + def copy(self) -> Self: """ Return a copy of the array. @@ -1077,14 +1068,14 @@ def shift(self, periods: int = 1, fill_value: object = None) -> IntervalArray: return self._concat_same_type([a, b]) def take( - self: IntervalArrayT, + self, indices, *, allow_fill: bool = False, fill_value=None, axis=None, **kwargs, - ) -> IntervalArrayT: + ) -> Self: """ Take elements from the IntervalArray. @@ -1434,7 +1425,7 @@ def closed(self) -> IntervalClosedType: ), } ) - def set_closed(self: IntervalArrayT, closed: IntervalClosedType) -> IntervalArrayT: + def set_closed(self, closed: IntervalClosedType) -> Self: if closed not in VALID_CLOSED: msg = f"invalid option for 'closed': {closed}" raise ValueError(msg) @@ -1590,7 +1581,7 @@ def _putmask(self, mask: npt.NDArray[np.bool_], value) -> None: assert not isinstance(self._right, np.ndarray) self._right._putmask(mask, value_right) - def insert(self: IntervalArrayT, loc: int, item: Interval) -> IntervalArrayT: + def insert(self, loc: int, item: Interval) -> Self: """ Return a new IntervalArray inserting new item at location. Follows Python numpy.insert semantics for negative values. Only Interval @@ -1612,7 +1603,7 @@ def insert(self: IntervalArrayT, loc: int, item: Interval) -> IntervalArrayT: return self._shallow_copy(new_left, new_right) - def delete(self: IntervalArrayT, loc) -> IntervalArrayT: + def delete(self, loc) -> Self: if isinstance(self._left, np.ndarray): new_left = np.delete(self._left, loc) assert isinstance(self._right, np.ndarray) @@ -1625,10 +1616,10 @@ def delete(self: IntervalArrayT, loc) -> IntervalArrayT: @Appender(_extension_array_shared_docs["repeat"] % _shared_docs_kwargs) def repeat( - self: IntervalArrayT, + self, repeats: int | Sequence[int], axis: AxisInt | None = None, - ) -> IntervalArrayT: + ) -> Self: nv.validate_repeat((), {"axis": axis}) left_repeat = self.left.repeat(repeats) right_repeat = self.right.repeat(repeats) diff --git a/pandas/core/arrays/masked.py b/pandas/core/arrays/masked.py index 0461b0f528878..8591cf2d3a4c5 100644 --- a/pandas/core/arrays/masked.py +++ b/pandas/core/arrays/masked.py @@ -6,7 +6,6 @@ Iterator, Literal, Sequence, - TypeVar, overload, ) import warnings @@ -30,6 +29,7 @@ PositionalIndexer, Scalar, ScalarIndexer, + Self, SequenceIndexer, Shape, npt, @@ -94,8 +94,6 @@ from pandas.compat.numpy import function as nv -BaseMaskedArrayT = TypeVar("BaseMaskedArrayT", bound="BaseMaskedArray") - class BaseMaskedArray(OpsMixin, ExtensionArray): """ @@ -134,9 +132,7 @@ def __init__( self._mask = mask @classmethod - def _from_sequence( - cls: type[BaseMaskedArrayT], scalars, *, dtype=None, copy: bool = False - ) -> BaseMaskedArrayT: + def _from_sequence(cls, scalars, *, dtype=None, copy: bool = False) -> Self: values, mask = cls._coerce_to_array(scalars, dtype=dtype, copy=copy) return cls(values, mask) @@ -149,12 +145,10 @@ def __getitem__(self, item: ScalarIndexer) -> Any: ... @overload - def __getitem__(self: BaseMaskedArrayT, item: SequenceIndexer) -> BaseMaskedArrayT: + def __getitem__(self, item: SequenceIndexer) -> Self: ... - def __getitem__( - self: BaseMaskedArrayT, item: PositionalIndexer - ) -> BaseMaskedArrayT | Any: + def __getitem__(self, item: PositionalIndexer) -> Self | Any: item = check_array_indexer(self, item) newmask = self._mask[item] @@ -167,9 +161,8 @@ def __getitem__( return type(self)(self._data[item], newmask) @doc(ExtensionArray.fillna) - def fillna( - self: BaseMaskedArrayT, value=None, method=None, limit=None - ) -> BaseMaskedArrayT: + @doc(ExtensionArray.fillna) + def fillna(self, value=None, method=None, limit=None) -> Self: value, method = validate_fillna_kwargs(value, method) mask = self._mask @@ -274,29 +267,29 @@ def shape(self) -> Shape: def ndim(self) -> int: return self._data.ndim - def swapaxes(self: BaseMaskedArrayT, axis1, axis2) -> BaseMaskedArrayT: + def swapaxes(self, axis1, axis2) -> Self: data = self._data.swapaxes(axis1, axis2) mask = self._mask.swapaxes(axis1, axis2) return type(self)(data, mask) - def delete(self: BaseMaskedArrayT, loc, axis: AxisInt = 0) -> BaseMaskedArrayT: + def delete(self, loc, axis: AxisInt = 0) -> Self: data = np.delete(self._data, loc, axis=axis) mask = np.delete(self._mask, loc, axis=axis) return type(self)(data, mask) - def reshape(self: BaseMaskedArrayT, *args, **kwargs) -> BaseMaskedArrayT: + def reshape(self, *args, **kwargs) -> Self: data = self._data.reshape(*args, **kwargs) mask = self._mask.reshape(*args, **kwargs) return type(self)(data, mask) - def ravel(self: BaseMaskedArrayT, *args, **kwargs) -> BaseMaskedArrayT: + def ravel(self, *args, **kwargs) -> Self: # TODO: need to make sure we have the same order for data/mask data = self._data.ravel(*args, **kwargs) mask = self._mask.ravel(*args, **kwargs) return type(self)(data, mask) @property - def T(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def T(self) -> Self: return type(self)(self._data.T, self._mask.T) def round(self, decimals: int = 0, *args, **kwargs): @@ -332,16 +325,16 @@ def round(self, decimals: int = 0, *args, **kwargs): # ------------------------------------------------------------------ # Unary Methods - def __invert__(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def __invert__(self) -> Self: return type(self)(~self._data, self._mask.copy()) - def __neg__(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def __neg__(self) -> Self: return type(self)(-self._data, self._mask.copy()) - def __pos__(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def __pos__(self) -> Self: return self.copy() - def __abs__(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def __abs__(self) -> Self: return type(self)(abs(self._data), self._mask.copy()) # ------------------------------------------------------------------ @@ -833,22 +826,22 @@ def nbytes(self) -> int: @classmethod def _concat_same_type( - cls: type[BaseMaskedArrayT], - to_concat: Sequence[BaseMaskedArrayT], + cls, + to_concat: Sequence[Self], axis: AxisInt = 0, - ) -> BaseMaskedArrayT: + ) -> Self: data = np.concatenate([x._data for x in to_concat], axis=axis) mask = np.concatenate([x._mask for x in to_concat], axis=axis) return cls(data, mask) def take( - self: BaseMaskedArrayT, + self, indexer, *, allow_fill: bool = False, fill_value: Scalar | None = None, axis: AxisInt = 0, - ) -> BaseMaskedArrayT: + ) -> Self: # we always fill with 1 internally # to avoid upcasting data_fill_value = self._internal_fill_value if isna(fill_value) else fill_value @@ -897,13 +890,13 @@ def isin(self, values) -> BooleanArray: # type: ignore[override] mask = np.zeros(self._data.shape, dtype=bool) return BooleanArray(result, mask, copy=False) - def copy(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def copy(self) -> Self: data, mask = self._data, self._mask data = data.copy() mask = mask.copy() return type(self)(data, mask, copy=False) - def unique(self: BaseMaskedArrayT) -> BaseMaskedArrayT: + def unique(self) -> Self: """ Compute the BaseMaskedArray of unique values. diff --git a/pandas/core/arrays/numeric.py b/pandas/core/arrays/numeric.py index 94eb101ea78df..b4b665d5264a7 100644 --- a/pandas/core/arrays/numeric.py +++ b/pandas/core/arrays/numeric.py @@ -6,7 +6,6 @@ Any, Callable, Mapping, - TypeVar, ) import numpy as np @@ -38,13 +37,11 @@ from pandas._typing import ( Dtype, DtypeObj, + Self, npt, ) -T = TypeVar("T", bound="NumericArray") - - class NumericDtype(BaseMaskedDtype): _default_np_dtype: np.dtype _checker: Callable[[Any], bool] # is_foo_dtype @@ -282,8 +279,8 @@ def _coerce_to_array( @classmethod def _from_sequence_of_strings( - cls: type[T], strings, *, dtype: Dtype | None = None, copy: bool = False - ) -> T: + cls, strings, *, dtype: Dtype | None = None, copy: bool = False + ) -> Self: from pandas.core.tools.numeric import to_numeric scalars = to_numeric(strings, errors="raise", dtype_backend="numpy_nullable") diff --git a/pandas/core/arrays/sparse/array.py b/pandas/core/arrays/sparse/array.py index ce9fdacf02c46..77dcfc463ed0c 100644 --- a/pandas/core/arrays/sparse/array.py +++ b/pandas/core/arrays/sparse/array.py @@ -12,7 +12,6 @@ Callable, Literal, Sequence, - TypeVar, cast, overload, ) @@ -114,6 +113,7 @@ class ellipsis(Enum): PositionalIndexer, Scalar, ScalarIndexer, + Self, SequenceIndexer, npt, ) @@ -127,8 +127,6 @@ class ellipsis(Enum): # ---------------------------------------------------------------------------- # Array -SparseArrayT = TypeVar("SparseArrayT", bound="SparseArray") - _sparray_doc_kwargs = {"klass": "SparseArray"} @@ -496,11 +494,11 @@ def __init__( @classmethod def _simple_new( - cls: type[SparseArrayT], + cls, sparse_array: np.ndarray, sparse_index: SparseIndex, dtype: SparseDtype, - ) -> SparseArrayT: + ) -> Self: new = object.__new__(cls) new._sparse_index = sparse_index new._sparse_values = sparse_array @@ -508,7 +506,7 @@ def _simple_new( return new @classmethod - def from_spmatrix(cls: type[SparseArrayT], data: spmatrix) -> SparseArrayT: + def from_spmatrix(cls, data: spmatrix) -> Self: """ Create a SparseArray from a scipy.sparse matrix. @@ -704,11 +702,11 @@ def isna(self): return type(self)(mask, fill_value=False, dtype=dtype) def fillna( - self: SparseArrayT, + self, value=None, method: FillnaOptions | None = None, limit: int | None = None, - ) -> SparseArrayT: + ) -> Self: """ Fill missing values with `value`. @@ -769,7 +767,7 @@ def fillna( return self._simple_new(new_values, self._sparse_index, new_dtype) - def shift(self: SparseArrayT, periods: int = 1, fill_value=None) -> SparseArrayT: + def shift(self, periods: int = 1, fill_value=None) -> Self: if not len(self) or periods == 0: return self.copy() @@ -817,7 +815,7 @@ def _first_fill_value_loc(self): diff = np.r_[np.diff(indices), 2] return indices[(diff > 1).argmax()] + 1 - def unique(self: SparseArrayT) -> SparseArrayT: + def unique(self) -> Self: uniques = algos.unique(self.sp_values) if len(self.sp_values) != len(self): fill_loc = self._first_fill_value_loc() @@ -899,15 +897,15 @@ def __getitem__(self, key: ScalarIndexer) -> Any: @overload def __getitem__( - self: SparseArrayT, + self, key: SequenceIndexer | tuple[int | ellipsis, ...], - ) -> SparseArrayT: + ) -> Self: ... def __getitem__( - self: SparseArrayT, + self, key: PositionalIndexer | tuple[int | ellipsis, ...], - ) -> SparseArrayT | Any: + ) -> Self | Any: if isinstance(key, tuple): key = unpack_tuple_and_ellipses(key) if key is Ellipsis: @@ -1009,9 +1007,7 @@ def _get_val_at(self, loc): val = maybe_box_datetimelike(val, self.sp_values.dtype) return val - def take( - self: SparseArrayT, indices, *, allow_fill: bool = False, fill_value=None - ) -> SparseArrayT: + def take(self, indices, *, allow_fill: bool = False, fill_value=None) -> Self: if is_scalar(indices): raise ValueError(f"'indices' must be an array, not a scalar '{indices}'.") indices = np.asarray(indices, dtype=np.int32) @@ -1094,7 +1090,7 @@ def _take_with_fill(self, indices, fill_value=None) -> np.ndarray: return taken - def _take_without_fill(self: SparseArrayT, indices) -> SparseArrayT: + def _take_without_fill(self, indices) -> Self: to_shift = indices < 0 n = len(self) @@ -1130,14 +1126,12 @@ def searchsorted( v = np.asarray(v) return np.asarray(self, dtype=self.dtype.subtype).searchsorted(v, side, sorter) - def copy(self: SparseArrayT) -> SparseArrayT: + def copy(self) -> Self: values = self.sp_values.copy() return self._simple_new(values, self.sp_index, self.dtype) @classmethod - def _concat_same_type( - cls: type[SparseArrayT], to_concat: Sequence[SparseArrayT] - ) -> SparseArrayT: + def _concat_same_type(cls, to_concat: Sequence[Self]) -> Self: fill_value = to_concat[0].fill_value values = [] @@ -1271,7 +1265,7 @@ def astype(self, dtype: AstypeArg | None = None, copy: bool = True): return self._simple_new(sp_values, self.sp_index, dtype) - def map(self: SparseArrayT, mapper, na_action=None) -> SparseArrayT: + def map(self, mapper, na_action=None) -> Self: """ Map categories using an input mapping or function.