summaryrefslogtreecommitdiff
path: root/numpy/lib
diff options
context:
space:
mode:
authorStephan Hoyer <shoyer@gmail.com>2017-04-27 12:17:06 -0700
committerCharles Harris <charlesr.harris@gmail.com>2017-04-27 13:37:51 -0600
commit32221dfb553980e34a398c71891c7dcdfaf2f477 (patch)
tree7ec8fceb73ae19451d75ff104c19f4d43fcd0ce9 /numpy/lib
parent3272a860129a7192a0e499c59e273da3dd35d998 (diff)
downloadpython-numpy-32221dfb553980e34a398c71891c7dcdfaf2f477.tar.gz
python-numpy-32221dfb553980e34a398c71891c7dcdfaf2f477.tar.bz2
python-numpy-32221dfb553980e34a398c71891c7dcdfaf2f477.zip
ENH: NDArrayOperatorsMixin calls ufuncs directly, like ndarray
* ENH: NDArrayOperatorsMixin calls ufuncs directly, like ndarray Per our discussion in https://github.com/numpy/numpy/pull/8247#discussion_r112825050 * add back in accidentally dropped __repr__
Diffstat (limited to 'numpy/lib')
-rw-r--r--numpy/lib/mixins.py59
-rw-r--r--numpy/lib/tests/test_mixins.py17
2 files changed, 36 insertions, 40 deletions
diff --git a/numpy/lib/mixins.py b/numpy/lib/mixins.py
index 2deb58827..21e4b346f 100644
--- a/numpy/lib/mixins.py
+++ b/numpy/lib/mixins.py
@@ -5,53 +5,54 @@ import sys
from numpy.core import umath as um
-# None of this module should be exposed in top-level NumPy module.
+# Nothing should be exposed in the top-level NumPy module.
__all__ = []
+def _disables_array_ufunc(obj):
+ """True when __array_ufunc__ is set to None."""
+ try:
+ return obj.__array_ufunc__ is None
+ except AttributeError:
+ return False
+
+
def _binary_method(ufunc):
+ """Implement a forward binary method with a ufunc, e.g., __add__."""
def func(self, other):
- try:
- if other.__array_ufunc__ is None:
- return NotImplemented
- except AttributeError:
- pass
- return self.__array_ufunc__(ufunc, '__call__', self, other)
+ if _disables_array_ufunc(other):
+ return NotImplemented
+ return ufunc(self, other)
return func
def _reflected_binary_method(ufunc):
+ """Implement a reflected binary method with a ufunc, e.g., __radd__."""
def func(self, other):
- try:
- if other.__array_ufunc__ is None:
- return NotImplemented
- except AttributeError:
- pass
- return self.__array_ufunc__(ufunc, '__call__', other, self)
+ if _disables_array_ufunc(other):
+ return NotImplemented
+ return ufunc(other, self)
return func
def _inplace_binary_method(ufunc):
+ """Implement an in-place binary method with a ufunc, e.g., __iadd__."""
def func(self, other):
- result = self.__array_ufunc__(
- ufunc, '__call__', self, other, out=(self,))
- if result is NotImplemented:
- raise TypeError('unsupported operand types for in-place '
- 'arithmetic: %s and %s'
- % (type(self).__name__, type(other).__name__))
- return result
+ return ufunc(self, other, out=(self,))
return func
def _numeric_methods(ufunc):
+ """Implement forward, reflected and inplace binary methods with a ufunc."""
return (_binary_method(ufunc),
_reflected_binary_method(ufunc),
_inplace_binary_method(ufunc))
def _unary_method(ufunc):
+ """Implement a unary special method with a ufunc."""
def func(self):
- return self.__array_ufunc__(ufunc, '__call__', self)
+ return ufunc(self)
return func
@@ -88,18 +89,14 @@ class NDArrayOperatorsMixin(object):
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
out = kwargs.get('out', ())
for x in inputs + out:
- # Only support operations with instances of _HANDLED_TYPES,
- # or instances of ArrayLike that are superclasses of this
- # object's type.
- if not (isinstance(x, self._HANDLED_TYPES) or
- (isinstance(x, ArrayLike) and
- isinstance(self, type(x)))):
+ # Only support operations with instances of _HANDLED_TYPES.
+ # Use ArrayLike instead of type(self) for isinstance to
+ # allow subclasses that don't override __array_ufunc__ to
+ # handle ArrayLike objects.
+ if not isinstance(x, self._HANDLED_TYPES + (ArrayLike,)):
return NotImplemented
# Defer to the implementation of the ufunc on unwrapped values.
- # Use ArrayLike instead of type(self) for isinstance to allow
- # subclasses that don't override __array_ufunc__ to handle
- # ArrayLike objects.
inputs = tuple(x.value if isinstance(x, ArrayLike) else x
for x in inputs)
if out:
@@ -138,6 +135,8 @@ class NDArrayOperatorsMixin(object):
with arbitrary, unrecognized types. This ensures that interactions with
ArrayLike preserve a well-defined casting hierarchy.
"""
+ # Like np.ndarray, this mixin class implements "Option 1" from the ufunc
+ # overrides NEP.
# comparisons don't have reflected and in-place versions
__lt__ = _binary_method(um.less)
diff --git a/numpy/lib/tests/test_mixins.py b/numpy/lib/tests/test_mixins.py
index bca974fc5..57c4a4cd8 100644
--- a/numpy/lib/tests/test_mixins.py
+++ b/numpy/lib/tests/test_mixins.py
@@ -26,18 +26,14 @@ class ArrayLike(np.lib.mixins.NDArrayOperatorsMixin):
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
out = kwargs.get('out', ())
for x in inputs + out:
- # Only support operations with instances of _HANDLED_TYPES,
- # or instances of ArrayLike that are superclasses of this
- # object's type.
- if not (isinstance(x, self._HANDLED_TYPES) or
- (isinstance(x, ArrayLike) and
- isinstance(self, type(x)))):
+ # Only support operations with instances of _HANDLED_TYPES.
+ # Use ArrayLike instead of type(self) for isinstance to
+ # allow subclasses that don't override __array_ufunc__ to
+ # handle ArrayLike objects.
+ if not isinstance(x, self._HANDLED_TYPES + (ArrayLike,)):
return NotImplemented
# Defer to the implementation of the ufunc on unwrapped values.
- # Use ArrayLike instead of type(self) for isinstance to allow
- # subclasses that don't override __array_ufunc__ to handle
- # ArrayLike objects.
inputs = tuple(x.value if isinstance(x, ArrayLike) else x
for x in inputs)
if out:
@@ -136,11 +132,12 @@ class TestNDArrayOperatorsMixin(TestCase):
def test_object(self):
x = ArrayLike(0)
obj = object()
- assert_equal(x.__add__(obj), NotImplemented)
with assert_raises(TypeError):
x + obj
with assert_raises(TypeError):
obj + x
+ with assert_raises(TypeError):
+ x += obj
def test_unary_methods(self):
array = np.array([-1, 0, 1, 2])