diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 16d2eaa410637..79272c5643281 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -5,7 +5,7 @@ """ import datetime import operator -from typing import Tuple +from typing import Tuple, Union import numpy as np @@ -13,7 +13,12 @@ from pandas.util._decorators import Appender from pandas.core.dtypes.common import is_list_like, is_timedelta64_dtype -from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries +from pandas.core.dtypes.generic import ( + ABCDataFrame, + ABCExtensionArray, + ABCIndexClass, + ABCSeries, +) from pandas.core.dtypes.missing import isna from pandas.core.construction import extract_array @@ -436,13 +441,37 @@ def _align_method_SERIES(left, right, align_asobject=False): return left, right -def _construct_result(left, result, index, name, dtype=None): +def _construct_result( + left: ABCSeries, + result: Union[np.ndarray, ABCExtensionArray], + index: ABCIndexClass, + name, +): """ - If the raw op result has a non-None name (e.g. it is an Index object) and - the name argument is None, then passing name to the constructor will - not be enough; we still need to override the name attribute. + Construct an appropriately-labelled Series from the result of an op. + + Parameters + ---------- + left : Series + result : ndarray or ExtensionArray + index : Index + name : object + + Returns + ------- + Series + In the case of __divmod__ or __rdivmod__, a 2-tuple of Series. """ - out = left._constructor(result, index=index, dtype=dtype) + if isinstance(result, tuple): + # produced by divmod or rdivmod + return ( + _construct_result(left, result[0], index=index, name=name), + _construct_result(left, result[1], index=index, name=name), + ) + + # We do not pass dtype to ensure that the Series constructor + # does inference in the case where `result` has object-dtype. + out = left._constructor(result, index=index) out = out.__finalize__(left) # Set the result's name after __finalize__ is called because __finalize__ @@ -451,15 +480,6 @@ def _construct_result(left, result, index, name, dtype=None): return out -def _construct_divmod_result(left, result, index, name, dtype=None): - """divmod returns a tuple of like indexed series instead of a single series. - """ - return ( - _construct_result(left, result[0], index=index, name=name, dtype=dtype), - _construct_result(left, result[1], index=index, name=name, dtype=dtype), - ) - - def _arith_method_SERIES(cls, op, special): """ Wrapper function for Series arithmetic operations, to avoid @@ -468,9 +488,6 @@ def _arith_method_SERIES(cls, op, special): str_rep = _get_opstr(op) op_name = _get_op_name(op, special) eval_kwargs = _gen_eval_kwargs(op_name) - construct_result = ( - _construct_divmod_result if op in [divmod, rdivmod] else _construct_result - ) def wrapper(left, right): if isinstance(right, ABCDataFrame): @@ -482,9 +499,7 @@ def wrapper(left, right): lvalues = extract_array(left, extract_numpy=True) result = arithmetic_op(lvalues, right, op, str_rep, eval_kwargs) - # We do not pass dtype to ensure that the Series constructor - # does inference in the case where `result` has object-dtype. - return construct_result(left, result, index=left.index, name=res_name) + return _construct_result(left, result, index=left.index, name=res_name) wrapper.__name__ = op_name return wrapper @@ -553,6 +568,7 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): # validate axis if axis is not None: self._get_axis_number(axis) + if isinstance(other, ABCSeries): return self._binop(other, op, level=level, fill_value=fill_value) elif isinstance(other, (np.ndarray, list, tuple)): @@ -564,7 +580,7 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0): if fill_value is not None: self = self.fillna(fill_value) - return self._constructor(op(self, other), self.index).__finalize__(self) + return op(self, other) flex_wrapper.__name__ = name return flex_wrapper diff --git a/pandas/core/series.py b/pandas/core/series.py index c87e371354f63..276f829d287ab 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2738,10 +2738,7 @@ def _binop(self, other, func, level=None, fill_value=None): result = func(this_vals, other_vals) name = ops.get_op_result_name(self, other) - if func.__name__ in ["divmod", "rdivmod"]: - ret = ops._construct_divmod_result(self, result, new_index, name) - else: - ret = ops._construct_result(self, result, new_index, name) + ret = ops._construct_result(self, result, new_index, name) return ret def combine(self, other, func, fill_value=None):