Skip to content

Commit 075f166

Browse files
jbrockmendelproost
authored andcommitted
REF: boilerplate for ops internal consistency (pandas-dev#28037)
1 parent a0f3175 commit 075f166

File tree

11 files changed

+105
-83
lines changed

11 files changed

+105
-83
lines changed

pandas/core/arrays/categorical.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from pandas._config import get_option
1010

11-
from pandas._libs import algos as libalgos, hashtable as htable, lib
11+
from pandas._libs import algos as libalgos, hashtable as htable
1212
from pandas.compat.numpy import function as nv
1313
from pandas.util._decorators import (
1414
Appender,
@@ -39,7 +39,7 @@
3939
needs_i8_conversion,
4040
)
4141
from pandas.core.dtypes.dtypes import CategoricalDtype
42-
from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries
42+
from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries
4343
from pandas.core.dtypes.inference import is_hashable
4444
from pandas.core.dtypes.missing import isna, notna
4545

@@ -52,6 +52,7 @@
5252
import pandas.core.common as com
5353
from pandas.core.construction import array, extract_array, sanitize_array
5454
from pandas.core.missing import interpolate_2d
55+
from pandas.core.ops.common import unpack_zerodim_and_defer
5556
from pandas.core.sorting import nargsort
5657

5758
from pandas.io.formats import console
@@ -74,16 +75,14 @@
7475
def _cat_compare_op(op):
7576
opname = "__{op}__".format(op=op.__name__)
7677

78+
@unpack_zerodim_and_defer(opname)
7779
def f(self, other):
7880
# On python2, you can usually compare any type to any type, and
7981
# Categoricals can be seen as a custom type, but having different
8082
# results depending whether categories are the same or not is kind of
8183
# insane, so be a bit stricter here and use the python3 idea of
8284
# comparing only things of equal type.
83-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
84-
return NotImplemented
8585

86-
other = lib.item_from_zerodim(other)
8786
if is_list_like(other) and len(other) != len(self):
8887
# TODO: Could this fail if the categories are listlike objects?
8988
raise ValueError("Lengths must match.")

pandas/core/arrays/datetimelike.py

+8-16
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,15 @@
3333
is_unsigned_integer_dtype,
3434
pandas_dtype,
3535
)
36-
from pandas.core.dtypes.generic import (
37-
ABCDataFrame,
38-
ABCIndexClass,
39-
ABCPeriodArray,
40-
ABCSeries,
41-
)
36+
from pandas.core.dtypes.generic import ABCIndexClass, ABCPeriodArray, ABCSeries
4237
from pandas.core.dtypes.inference import is_array_like
4338
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna
4439

4540
from pandas._typing import DatetimeLikeScalar
4641
from pandas.core import missing, nanops
4742
from pandas.core.algorithms import checked_add_with_arr, take, unique1d, value_counts
4843
import pandas.core.common as com
44+
from pandas.core.ops.common import unpack_zerodim_and_defer
4945
from pandas.core.ops.invalid import make_invalid_op
5046

5147
from pandas.tseries import frequencies
@@ -1194,13 +1190,11 @@ def _time_shift(self, periods, freq=None):
11941190
# to be passed explicitly.
11951191
return self._generate_range(start=start, end=end, periods=None, freq=self.freq)
11961192

1193+
@unpack_zerodim_and_defer("__add__")
11971194
def __add__(self, other):
1198-
other = lib.item_from_zerodim(other)
1199-
if isinstance(other, (ABCSeries, ABCDataFrame, ABCIndexClass)):
1200-
return NotImplemented
12011195

12021196
# scalar others
1203-
elif other is NaT:
1197+
if other is NaT:
12041198
result = self._add_nat()
12051199
elif isinstance(other, (Tick, timedelta, np.timedelta64)):
12061200
result = self._add_delta(other)
@@ -1248,13 +1242,11 @@ def __radd__(self, other):
12481242
# alias for __add__
12491243
return self.__add__(other)
12501244

1245+
@unpack_zerodim_and_defer("__sub__")
12511246
def __sub__(self, other):
1252-
other = lib.item_from_zerodim(other)
1253-
if isinstance(other, (ABCSeries, ABCDataFrame, ABCIndexClass)):
1254-
return NotImplemented
12551247

12561248
# scalar others
1257-
elif other is NaT:
1249+
if other is NaT:
12581250
result = self._sub_nat()
12591251
elif isinstance(other, (Tick, timedelta, np.timedelta64)):
12601252
result = self._add_delta(-other)
@@ -1343,11 +1335,11 @@ def __rsub__(self, other):
13431335
return -(self - other)
13441336

13451337
# FIXME: DTA/TDA/PA inplace methods should actually be inplace, GH#24115
1346-
def __iadd__(self, other):
1338+
def __iadd__(self, other): # type: ignore
13471339
# alias for __add__
13481340
return self.__add__(other)
13491341

1350-
def __isub__(self, other):
1342+
def __isub__(self, other): # type: ignore
13511343
# alias for __sub__
13521344
return self.__sub__(other)
13531345

pandas/core/arrays/datetimes.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,15 @@
4040
pandas_dtype,
4141
)
4242
from pandas.core.dtypes.dtypes import DatetimeTZDtype
43-
from pandas.core.dtypes.generic import (
44-
ABCDataFrame,
45-
ABCIndexClass,
46-
ABCPandasArray,
47-
ABCSeries,
48-
)
43+
from pandas.core.dtypes.generic import ABCIndexClass, ABCPandasArray, ABCSeries
4944
from pandas.core.dtypes.missing import isna
5045

5146
from pandas.core import ops
5247
from pandas.core.algorithms import checked_add_with_arr
5348
from pandas.core.arrays import datetimelike as dtl
5449
from pandas.core.arrays._ranges import generate_regular_range
5550
import pandas.core.common as com
51+
from pandas.core.ops.common import unpack_zerodim_and_defer
5652
from pandas.core.ops.invalid import invalid_comparison
5753

5854
from pandas.tseries.frequencies import get_period_alias, to_offset
@@ -157,11 +153,8 @@ def _dt_array_cmp(cls, op):
157153
opname = "__{name}__".format(name=op.__name__)
158154
nat_result = opname == "__ne__"
159155

156+
@unpack_zerodim_and_defer(opname)
160157
def wrapper(self, other):
161-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
162-
return NotImplemented
163-
164-
other = lib.item_from_zerodim(other)
165158

166159
if isinstance(other, (datetime, np.datetime64, str)):
167160
if isinstance(other, (datetime, np.datetime64)):

pandas/core/arrays/integer.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
is_scalar,
2222
)
2323
from pandas.core.dtypes.dtypes import register_extension_dtype
24-
from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries
2524
from pandas.core.dtypes.missing import isna, notna
2625

2726
from pandas.core import nanops, ops
2827
from pandas.core.algorithms import take
2928
from pandas.core.arrays import ExtensionArray, ExtensionOpsMixin
29+
from pandas.core.ops.common import unpack_zerodim_and_defer
3030
from pandas.core.tools.numeric import to_numeric
3131

3232

@@ -602,13 +602,8 @@ def _values_for_argsort(self) -> np.ndarray:
602602
def _create_comparison_method(cls, op):
603603
op_name = op.__name__
604604

605+
@unpack_zerodim_and_defer(op.__name__)
605606
def cmp_method(self, other):
606-
607-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
608-
# Rely on pandas to unbox and dispatch to us.
609-
return NotImplemented
610-
611-
other = lib.item_from_zerodim(other)
612607
mask = None
613608

614609
if isinstance(other, IntegerArray):
@@ -697,15 +692,14 @@ def _maybe_mask_result(self, result, mask, other, op_name):
697692
def _create_arithmetic_method(cls, op):
698693
op_name = op.__name__
699694

695+
@unpack_zerodim_and_defer(op.__name__)
700696
def integer_arithmetic_method(self, other):
701697

702-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
703-
# Rely on pandas to unbox and dispatch to us.
704-
return NotImplemented
705-
706-
other = lib.item_from_zerodim(other)
707698
mask = None
708699

700+
if getattr(other, "ndim", 0) > 1:
701+
raise NotImplementedError("can only perform ops with 1-d structures")
702+
709703
if isinstance(other, IntegerArray):
710704
other, mask = other._data, other._mask
711705

pandas/core/arrays/period.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import numpy as np
66

7-
from pandas._libs import lib
87
from pandas._libs.tslibs import (
98
NaT,
109
NaTType,
@@ -35,7 +34,6 @@
3534
)
3635
from pandas.core.dtypes.dtypes import PeriodDtype
3736
from pandas.core.dtypes.generic import (
38-
ABCDataFrame,
3937
ABCIndexClass,
4038
ABCPeriodArray,
4139
ABCPeriodIndex,
@@ -46,6 +44,7 @@
4644
import pandas.core.algorithms as algos
4745
from pandas.core.arrays import datetimelike as dtl
4846
import pandas.core.common as com
47+
from pandas.core.ops.common import unpack_zerodim_and_defer
4948

5049
from pandas.tseries import frequencies
5150
from pandas.tseries.offsets import DateOffset, Tick, _delta_to_tick
@@ -69,13 +68,10 @@ def _period_array_cmp(cls, op):
6968
opname = "__{name}__".format(name=op.__name__)
7069
nat_result = opname == "__ne__"
7170

71+
@unpack_zerodim_and_defer(opname)
7272
def wrapper(self, other):
7373
ordinal_op = getattr(self.asi8, opname)
7474

75-
other = lib.item_from_zerodim(other)
76-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
77-
return NotImplemented
78-
7975
if is_list_like(other) and len(other) != len(self):
8076
raise ValueError("Lengths must match")
8177

pandas/core/arrays/sparse/array.py

+4-15
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@
3434
is_string_dtype,
3535
pandas_dtype,
3636
)
37-
from pandas.core.dtypes.generic import (
38-
ABCDataFrame,
39-
ABCIndexClass,
40-
ABCSeries,
41-
ABCSparseArray,
42-
)
37+
from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries, ABCSparseArray
4338
from pandas.core.dtypes.missing import isna, na_value_for_dtype, notna
4439

4540
import pandas.core.algorithms as algos
@@ -49,6 +44,7 @@
4944
from pandas.core.construction import sanitize_array
5045
from pandas.core.missing import interpolate_2d
5146
import pandas.core.ops as ops
47+
from pandas.core.ops.common import unpack_zerodim_and_defer
5248

5349
import pandas.io.formats.printing as printing
5450

@@ -1410,12 +1406,8 @@ def sparse_unary_method(self):
14101406
def _create_arithmetic_method(cls, op):
14111407
op_name = op.__name__
14121408

1409+
@unpack_zerodim_and_defer(op_name)
14131410
def sparse_arithmetic_method(self, other):
1414-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
1415-
# Rely on pandas to dispatch to us.
1416-
return NotImplemented
1417-
1418-
other = lib.item_from_zerodim(other)
14191411

14201412
if isinstance(other, SparseArray):
14211413
return _sparse_array_op(self, other, op, op_name)
@@ -1463,12 +1455,9 @@ def _create_comparison_method(cls, op):
14631455
if op_name in {"and_", "or_"}:
14641456
op_name = op_name[:-1]
14651457

1458+
@unpack_zerodim_and_defer(op_name)
14661459
def cmp_method(self, other):
14671460

1468-
if isinstance(other, (ABCSeries, ABCIndexClass)):
1469-
# Rely on pandas to unbox and dispatch to us.
1470-
return NotImplemented
1471-
14721461
if not is_scalar(other) and not isinstance(other, type(self)):
14731462
# convert list-like to ndarray
14741463
other = np.asarray(other)

pandas/core/arrays/timedeltas.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from pandas.core import nanops
4646
from pandas.core.algorithms import checked_add_with_arr
4747
import pandas.core.common as com
48+
from pandas.core.ops.common import unpack_zerodim_and_defer
4849
from pandas.core.ops.invalid import invalid_comparison
4950

5051
from pandas.tseries.frequencies import to_offset
@@ -82,10 +83,8 @@ def _td_array_cmp(cls, op):
8283
opname = "__{name}__".format(name=op.__name__)
8384
nat_result = opname == "__ne__"
8485

86+
@unpack_zerodim_and_defer(opname)
8587
def wrapper(self, other):
86-
other = lib.item_from_zerodim(other)
87-
if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)):
88-
return NotImplemented
8988

9089
if _is_convertible_to_td(other) or other is NaT:
9190
try:

pandas/core/indexes/range.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
is_scalar,
2222
is_timedelta64_dtype,
2323
)
24-
from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries, ABCTimedeltaIndex
24+
from pandas.core.dtypes.generic import ABCTimedeltaIndex
2525

2626
from pandas.core import ops
2727
import pandas.core.common as com
2828
from pandas.core.construction import extract_array
2929
import pandas.core.indexes.base as ibase
3030
from pandas.core.indexes.base import Index, _index_shared_docs
3131
from pandas.core.indexes.numeric import Int64Index
32+
from pandas.core.ops.common import unpack_zerodim_and_defer
3233

3334
from pandas.io.formats.printing import pprint_thing
3435

@@ -731,9 +732,8 @@ def __getitem__(self, key):
731732
# fall back to Int64Index
732733
return super().__getitem__(key)
733734

735+
@unpack_zerodim_and_defer("__floordiv__")
734736
def __floordiv__(self, other):
735-
if isinstance(other, (ABCSeries, ABCDataFrame)):
736-
return NotImplemented
737737

738738
if is_integer(other) and other != 0:
739739
if len(self) == 0 or self.start % other == 0 and self.step % other == 0:
@@ -769,10 +769,9 @@ def _make_evaluate_binop(op, step=False):
769769
if False, use the existing step
770770
"""
771771

772+
@unpack_zerodim_and_defer(op.__name__)
772773
def _evaluate_numeric_binop(self, other):
773-
if isinstance(other, (ABCSeries, ABCDataFrame)):
774-
return NotImplemented
775-
elif isinstance(other, ABCTimedeltaIndex):
774+
if isinstance(other, ABCTimedeltaIndex):
776775
# Defer to TimedeltaIndex implementation
777776
return NotImplemented
778777
elif isinstance(other, (timedelta, np.timedelta64)):

pandas/core/ops/__init__.py

+4-10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
logical_op,
3030
)
3131
from pandas.core.ops.array_ops import comp_method_OBJECT_ARRAY # noqa:F401
32+
from pandas.core.ops.common import unpack_zerodim_and_defer
3233
from pandas.core.ops.dispatch import maybe_dispatch_ufunc_to_dunder_op # noqa:F401
3334
from pandas.core.ops.dispatch import should_series_dispatch
3435
from pandas.core.ops.docstrings import (
@@ -489,9 +490,8 @@ def _arith_method_SERIES(cls, op, special):
489490
op_name = _get_op_name(op, special)
490491
eval_kwargs = _gen_eval_kwargs(op_name)
491492

493+
@unpack_zerodim_and_defer(op_name)
492494
def wrapper(left, right):
493-
if isinstance(right, ABCDataFrame):
494-
return NotImplemented
495495

496496
left, right = _align_method_SERIES(left, right)
497497
res_name = get_op_result_name(left, right)
@@ -512,14 +512,11 @@ def _comp_method_SERIES(cls, op, special):
512512
"""
513513
op_name = _get_op_name(op, special)
514514

515+
@unpack_zerodim_and_defer(op_name)
515516
def wrapper(self, other):
516517

517518
res_name = get_op_result_name(self, other)
518519

519-
if isinstance(other, ABCDataFrame): # pragma: no cover
520-
# Defer to DataFrame implementation; fail early
521-
return NotImplemented
522-
523520
if isinstance(other, ABCSeries) and not self._indexed_same(other):
524521
raise ValueError("Can only compare identically-labeled Series objects")
525522

@@ -541,14 +538,11 @@ def _bool_method_SERIES(cls, op, special):
541538
"""
542539
op_name = _get_op_name(op, special)
543540

541+
@unpack_zerodim_and_defer(op_name)
544542
def wrapper(self, other):
545543
self, other = _align_method_SERIES(self, other, align_asobject=True)
546544
res_name = get_op_result_name(self, other)
547545

548-
if isinstance(other, ABCDataFrame):
549-
# Defer to DataFrame implementation; fail early
550-
return NotImplemented
551-
552546
lvalues = extract_array(self, extract_numpy=True)
553547
rvalues = extract_array(other, extract_numpy=True)
554548

0 commit comments

Comments
 (0)