-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
COMPAT: Return NotImplemented for subclassing #31136
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
COMPAT: Return NotImplemented for subclassing #31136
Conversation
This changes index ops to check the *type* of the argument in index ops, rather than just the dtype. This lets index subclasses take control of binary ops when they know better what the result should be. Closes pandas-dev#31109
Mmm, this is the wrong place to fix it. We've already unboxed the array in |
LGTM, azure error looks unrelated |
Just a regression on master. |
k merge on green |
@@ -110,6 +114,15 @@ def wrapper(self, other): | |||
|
|||
def make_wrapped_arith_op(opname): | |||
def method(self, other): | |||
if ( | |||
isinstance(other, Index) | |||
and is_object_dtype(other.dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only object dtype? You could have Index subclasses with a different dtype? (or is that not realistic?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's my suspicion. It's at least true for the xarray case.
@dcherian does xarray have any Index subclasses that have a non object
dtype?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a question for @shoyer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Xarray only has one Index subclass, which is of object dtype. It's definitely a little fragile, though, so we would like to eventually get rid of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we would like to eventually get rid of it.
IIRC CFTime is for the "UT" timezone, which is always within 1 second of UTC, right? Can this be represented as a tzinfo object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good
@@ -323,3 +324,51 @@ def test_rsub_object(self): | |||
|
|||
with pytest.raises(TypeError): | |||
np.array([True, pd.Timestamp.now()]) - index | |||
|
|||
@pytest.mark.parametrize( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should move this to:
tests/indexes/test_subclass.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see an indexes/test_subclass.py. Start a new file for this?
Moving the conversation in https://github.com/pandas-dev/pandas/pull/31136/files/35549e681845d8505250dda00a9c22cff915f67f#diff-8ccfea8b354e9d2156ebc3150f69a18b out of inline. Python has a behavior I hadn't realized before. Typically, for In [8]: class A:
...: def __add__(self, other):
...: print('a-add')
...: return 0
...: def __radd__(self, other):
...: print('a-radd')
...: return 1
...:
In [9]: class B:
...: def __add__(self, other):
...: print('b-add')
...: return 2
...: def __radd__(self, other):
...: print("b-radd")
...: return 3
...:
In [10]: A() + B()
a-add
Out[10]: 0
In [11]: class B(A): # subclass
...: def __add__(self, other):
...: print('b-add')
...: return 2
...: def __radd__(self, other):
...: print("b-radd")
...: return 3
...:
In [12]: A() + B()
b-radd
Out[12]: 3 This means that an object dtype Index would have deferred to an Index subclass, just implicitly since Python does that. So numeric is the only behavior change we need to consider. |
Numeric shouldn't be relevant for the CFDatetimeIndex case of interest right? I think the fix here should stay narrow and targeted at that for 1.0. |
@jorisvandenbossche can you comment on #31136 (comment)? Does it address your concerns in #31136 (comment)? |
Merging later today if there aren't any objections. |
You mean because if you subclass |
All this is correct. As an example, if you have class MyIndex(Index):
pass Then
This is where I think we disagree. In general, I don't think that all our Index subclasses can / should be doing these checks. Ideally in the future we have an ExtensionIndex API and CFTimeIndex is an instance of that. But for now I think we should just restore the 0.25 behavior. |
Ah, sorry, I thought this was what you were doing in this PR. So now you do the check not in general for numeric index, but only for EA-based indexes (to have it for datetime-like indexes) ? |
Anything using |
OK, I was just going from the fact it being in Anyway, I am fine with focusing on getting the compatibility with 0.25. |
@meeseeksdev backport to 1.0.x |
…1405) Co-authored-by: Tom Augspurger <[email protected]>
This changes index ops to check the type of the argument in index
ops, rather than just the dtype. This lets index subclasses take control
of binary ops when they know better what the result should be.
Closes #31109
cc @jbrockmendel, I've fixed this as close to the root of the problem as I could, but perhaps this case should be checked elsewhere? As written currently, it only affects add & sub between an Index and a datelike index, but perhaps it should be done more generally?