You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Ufunc overrides should always raise TypeError if any arguments set __array_ufunc__ = None #9011
Following up on our discussion in #8247 (comment), we need to switch ufunc overrides to always raise TypeError if any arguments set __array_ufunc__ = None. The avoids potential for confusion with x + y and np.add(x, y) yielding different results.
I'm still not sure I entirely agree with this: at least my original intention was for __array_ufunc__ = None to mean that ndarray could not deal with the class, i.e., it was meant as a replacement for setting __array_priority__ to a very high value. I'm not sure we really need to protect implementers of their own __array_ufunc__ from making mistakes, and thus also preventing them from possibly handling such objects. (Though in practice it may just mean defining a few binary methods.)
@mhvk There wasn't a formal vote, but my impression was that the majority thought is should simply raise a TypeError in this situation. In practice, I doubt it will make much difference, so the simplest behavior has much to recommend it. The use of big __array_priority__ values was a workaround that abused the functionality as there was no other way to achieve the desired behaviour.
@mhvk: Consider np.add(a, b), where a sets __array_ufunc__ = None. In current master, this will always raise an error, regardless of how b implements __array_ufunc__. So I don't think there's much value in making it possible for np.add(b, a) to return something different?
(Well, I guess if a.__array_ufunc__ is None and b is a proper subclass of a that defines a real __array_ufunc__ method then np.add(a, b) could work but ughghgg my head hurts. It's just easier to think about everything if __array_ufunc__ = None means it doesn't participate in ufuncs, with no complicated exceptions that need documenting.)
In current master, if b also implements __array_ufunc__ and can deal with a, it will in fact work (if I'm wrong in this, then I implemented it wrong...). I have to admit I cannot think of a good example of where this will be useful; it is more a feeling that a class should not be allowed to determine that something doesn't work if it also involves another class; all it can say is that it itself cannot handle ufuncs -- i.e., __array_ufunc__ = None is just a short-cut for return NotImplemented which allows one to avoid calling it -- and for ndarray we draw the logical conclusion that we get a TypeError for the ufunc and NotImplemented for the operators.
Here, I think part of the differences arises from my feeling that the ufunc module should effectively be more like operator, with the actual implementation in C considered part of ndarray, not of the ufunc itself (I really hope to do this in follow-up...). This means that the ufunc module always tries all operands, even if one of them already signals ahead it cannot do it.
Continuing on the above: there is no direct precedent from python, but I think one should treat __array_ufunc__ = None the same as the absence of an operator special method, which in python is treated as equivalent to returning NotImplemented, i.e., it does not preclude another class from having the reverse method and doing something. (For ufuncs we cannot do the same thing since obviously the absence of __array_ufunc__ simply invokes the legacy mechanism.)
In the end, I do think it boils down to the question of whether one treats the ufunc module as a standard interface that numpy provides to evaluate mathematical functions, i.e., whether as I suggested above it is similar to operator (arguably, math should provide overrides, but that would seem a bit of a pipe dream...).
As noted in #9014, I'd still prefer sticking with what we have, but definitely do not feel very strongly about it. So, fine to go with the stricter rules.
Activity
mhvk commentedon Apr 27, 2017
I'm still not sure I entirely agree with this: at least my original intention was for
__array_ufunc__ = Noneto mean that ndarray could not deal with the class, i.e., it was meant as a replacement for setting__array_priority__to a very high value. I'm not sure we really need to protect implementers of their own__array_ufunc__from making mistakes, and thus also preventing them from possibly handling such objects. (Though in practice it may just mean defining a few binary methods.)charris commentedon Apr 27, 2017
@mhvk There wasn't a formal vote, but my impression was that the majority thought is should simply raise a TypeError in this situation. In practice, I doubt it will make much difference, so the simplest behavior has much to recommend it. The use of big
__array_priority__values was a workaround that abused the functionality as there was no other way to achieve the desired behaviour.njsmith commentedon Apr 29, 2017
@mhvk: Consider
np.add(a, b), whereasets__array_ufunc__ = None. In current master, this will always raise an error, regardless of howbimplements__array_ufunc__. So I don't think there's much value in making it possible fornp.add(b, a)to return something different?(Well, I guess if
a.__array_ufunc__ is Noneandbis a proper subclass ofathat defines a real__array_ufunc__method thennp.add(a, b)could work but ughghgg my head hurts. It's just easier to think about everything if__array_ufunc__ = Nonemeans it doesn't participate in ufuncs, with no complicated exceptions that need documenting.)mhvk commentedon Apr 29, 2017
In current master, if
balso implements__array_ufunc__and can deal witha, it will in fact work (if I'm wrong in this, then I implemented it wrong...). I have to admit I cannot think of a good example of where this will be useful; it is more a feeling that a class should not be allowed to determine that something doesn't work if it also involves another class; all it can say is that it itself cannot handle ufuncs -- i.e.,__array_ufunc__ = Noneis just a short-cut forreturn NotImplementedwhich allows one to avoid calling it -- and forndarraywe draw the logical conclusion that we get aTypeErrorfor the ufunc andNotImplementedfor the operators.Here, I think part of the differences arises from my feeling that the ufunc module should effectively be more like
operator, with the actual implementation in C considered part ofndarray, not of the ufunc itself (I really hope to do this in follow-up...). This means that the ufunc module always tries all operands, even if one of them already signals ahead it cannot do it.mhvk commentedon Apr 29, 2017
Continuing on the above: there is no direct precedent from python, but I think one should treat
__array_ufunc__ = Nonethe same as the absence of an operator special method, which in python is treated as equivalent to returningNotImplemented, i.e., it does not preclude another class from having the reverse method and doing something. (For ufuncs we cannot do the same thing since obviously the absence of__array_ufunc__simply invokes the legacy mechanism.)In the end, I do think it boils down to the question of whether one treats the ufunc module as a standard interface that numpy provides to evaluate mathematical functions, i.e., whether as I suggested above it is similar to
operator(arguably,mathshould provide overrides, but that would seem a bit of a pipe dream...).mhvk commentedon Apr 30, 2017
As noted in #9014, I'd still prefer sticking with what we have, but definitely do not feel very strongly about it. So, fine to go with the stricter rules.