Conversation
We don't call array-prepare consistently only for normal ufuncs, it doesn't seem used by many people (the main use-case seems units and masked-arrays, both of which are clearly much better served by `__array_ufunc__`, even a bad version of it). Now, there *is* one dance/change that needs to be OK, if the user does NOT implement `__array_prepare__`, we make it a true no-op now, while previously it would have done `arr.view(type=subclass)`. That matters in one case in the tests where a faulty `__array_wrap__` implementation expected to get a subclass.
| .. note:: If a class defines the :func:`__array_ufunc__` method, | ||
| this disables the :func:`__array_wrap__`, | ||
| :func:`__array_prepare__`, :data:`__array_priority__` mechanism | ||
| described below for ufuncs (which may eventually be deprecated). |
There was a problem hiding this comment.
Maybe someone disagrees that this comment is useless. We don't disable it, you just have to view() as a base ndarray to avoid recursion.
| If necessary, the result will be cast to the data-type(s) of the provided | ||
| output array(s). | ||
| If the output has an :obj:`~.class.__array_wrap__` method it is called instead | ||
| of the one found on the inputs. |
There was a problem hiding this comment.
I have no idea where that __array__ nonsense ever came from...
There was a problem hiding this comment.
Indeed, very weird! I think it predates my changes to this file...
| # The b is a special case because it is used for reconstructing. | ||
| if not _globalvar and self.dtype.char not in 'SUbc': | ||
| if self.dtype.char not in 'SUbc': | ||
| raise ValueError("Can only create a chararray from string data.") |
There was a problem hiding this comment.
mmap also had such a global once upon a time. I don't get it. If there is a super strange reason for why it's needed let's put it back when it shows up...
There was a problem hiding this comment.
Strange, indeed. It looks like it protects the actual creation of the array (which will call __array_finalize__), but I'm not sure what that is needed. And tests pass...
| if isinstance(obj, MyArray): | ||
| if not isinstance(obj, MyArray): | ||
| obj = obj.view(MyArray) | ||
| if obj.metadata is None: |
There was a problem hiding this comment.
This one is the one change, it now gets a plain array, not a subclass. The user can actually "detect" that case by checking if context!=None
| def _makearray(a): | ||
| new = asarray(a) | ||
| wrap = getattr(a, "__array_prepare__", new.__array_wrap__) | ||
| wrap = getattr(a, "__array_wrap__", new.__array_wrap__) |
There was a problem hiding this comment.
So, if anyone can tell me why this was changed to use array-prepare rather than array-wrap at some point, then...
There was a problem hiding this comment.
I always wondered about that one too...
|
This was originally introduced ~14 years ago by @ddale. But to me there is little logic in keeping this patch-work around. The one useful implementation I saw, had the purpose of raising an error on impossible ufunc calls with a The main point is: the use in linalg doesn't even make remotely sense to me. And dealing with ufuncs is easier with |
|
@mhvk astropy has an |
|
@seberg - I was about as surprised as you that there still was an |
There was a problem hiding this comment.
@seberg - nice to get rid of that one! My main question is whether there should be a full deprecation cycle. Right now, you remove/break ndarray.__array_prepare__ but still call out __array_prepare__ on inputs. But if a subclass does super() this will break. Might it be better to just remove it altogether? Or do a proper deprecation also of ndarray.__array_prepare__?
p.s. Numpy 2.0 perhaps give one a reason to just remove it!?
| If necessary, the result will be cast to the data-type(s) of the provided | ||
| output array(s). | ||
| If the output has an :obj:`~.class.__array_wrap__` method it is called instead | ||
| of the one found on the inputs. |
There was a problem hiding this comment.
Indeed, very weird! I think it predates my changes to this file...
| # The b is a special case because it is used for reconstructing. | ||
| if not _globalvar and self.dtype.char not in 'SUbc': | ||
| if self.dtype.char not in 'SUbc': | ||
| raise ValueError("Can only create a chararray from string data.") |
There was a problem hiding this comment.
Strange, indeed. It looks like it protects the actual creation of the array (which will call __array_finalize__), but I'm not sure what that is needed. And tests pass...
numpy/_core/src/umath/ufunc_object.c
Outdated
| PyObject *args_tup; | ||
|
|
||
| /* Use a deprecation warning, since end-users shouldn't worry. */ | ||
| if (DEPRECATE( |
There was a problem hiding this comment.
Hmm, this seems a bit strange: you deprecate the call out, but any subclass that does super().__array_prepare__(...) will now fail. Or am I missing something?
| def _makearray(a): | ||
| new = asarray(a) | ||
| wrap = getattr(a, "__array_prepare__", new.__array_wrap__) | ||
| wrap = getattr(a, "__array_wrap__", new.__array_wrap__) |
There was a problem hiding this comment.
I always wondered about that one too...
That was my first inclination, but then thought it's not pressing enough that I care about removing it out-right.
This is the best I could think of. If you inherit it, you must not get warnings, I don't think I want to add an |
|
+1 for a direct removal from me.
yup |
|
Rerunning the PyPy windows CI, which timed out after 60 minutes |
__array_prepare____array_prepare__
|
Fair, I don't really care either way, so added a commit to just out-right remove it. If we don't squash them, could go back to the less escalated version easier maybe. |
|
|
||
| /* The reset may copy the first buffer chunk, which could cause FPEs */ | ||
| if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { | ||
| if (NpyIter_Reset(iter, NULL) != NPY_SUCCEED) { |
There was a problem hiding this comment.
Due to delay bufalloc (which isneeded as per comment), need to always reset, but basepointers was only for crazy array-prep support.
688078d to
d3f0bb2
Compare
d3f0bb2 to
a72d735
Compare
mhvk
left a comment
There was a problem hiding this comment.
This looks good modulo the small comment on the docs; since that is quite trivial, I'll approve now.
Aside: should we deprecate __array_wrap__ for 2.0? I guess less easy since MaskedArray uses it, but it would be nice to get rid of it...
|
|
||
| .. note:: For ufuncs, it is hoped to eventually deprecate this method in | ||
| favour of :func:`__array_ufunc__`. | ||
| favour of :func:`__array_ufunc__` and :func:`__array_function__`. |
There was a problem hiding this comment.
This is not true for ufuncs, but I guess you were trying to include the few non-ufuncs that use it too? Since those are far fewer and really that is even more a strange leftover, maybe rewrite:
It is hoped to eventually deprecate this method in favour of
func:`__array_ufunc__` for ufuncs (and :func:`__array_function__`
for a few other functions like :func:`np.squeeze`).
There was a problem hiding this comment.
Adopted that. I am not even sure if we should care to ever remove it, although stopping to pass context could be on the table probably.
In a sense, there is a small use-case for very simple array-like objects, e.g. that do nothing but add a few methods or package a NumPy array. For those, __array_function__ is a bit trickier than it should be unfortunately.
Since I answered in the comment only. My answer would be "no". More importantly, I don't think it is ideal, but it isn't super unreasonable as long as your type doesn't drag metadata around (which makes it unreasonable for masked arrays of course). |
|
Carrying metadata using |
|
Thanks @seberg. |
We don't call array-prepare consistently only for normal ufuncs, it doesn't seem used by many people (the main use-case seems units and masked-arrays, both of which are clearly much better served by
__array_ufunc__, even a bad version of it).Now, there is one dance/change that needs to be OK, if the user does NOT implement
__array_prepare__, we make it a true no-op now, while previously it would have donearr.view(type=subclass).That matters in one case in the tests where a faulty
__array_wrap__implementation expected to get a subclass.