diff options
author | Stephan Hoyer <shoyer@gmail.com> | 2017-04-27 12:17:06 -0700 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2017-04-27 13:37:51 -0600 |
commit | 32221dfb553980e34a398c71891c7dcdfaf2f477 (patch) | |
tree | 7ec8fceb73ae19451d75ff104c19f4d43fcd0ce9 /numpy/lib | |
parent | 3272a860129a7192a0e499c59e273da3dd35d998 (diff) | |
download | python-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.py | 59 | ||||
-rw-r--r-- | numpy/lib/tests/test_mixins.py | 17 |
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]) |