diff options
author | Matteo Raso <matteo_luigi_raso@protonmail.com> | 2023-04-01 21:45:16 -0400 |
---|---|---|
committer | Matteo Raso <matteo_luigi_raso@protonmail.com> | 2023-04-01 21:45:16 -0400 |
commit | dfaa72d72453b8738ec711180e03da824651e46b (patch) | |
tree | 8e03ad1d58833e63b413d61209e9d4315c74d20a /numpy/lib/function_base.py | |
parent | 40bb77e8b7ee2db58f306d7f96e9cb6d18bb768f (diff) | |
download | numpy-dfaa72d72453b8738ec711180e03da824651e46b.tar.gz |
Fixed edge case where pyfunc has no attribute `__name__`
Diffstat (limited to 'numpy/lib/function_base.py')
-rw-r--r-- | numpy/lib/function_base.py | 105 |
1 files changed, 66 insertions, 39 deletions
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index f0f374f97..b9f0d58d2 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -24,7 +24,7 @@ from numpy.core import overrides from numpy.core.function_base import add_newdoc from numpy.lib.twodim_base import diag from numpy.core.multiarray import ( - _place, add_docstring, bincount, normalize_axis_index, _monotonicity, + _insert, add_docstring, bincount, normalize_axis_index, _monotonicity, interp as compiled_interp, interp_complex as compiled_interp_complex ) from numpy.core.umath import _add_newdoc_ufunc as add_newdoc_ufunc @@ -1311,8 +1311,6 @@ def gradient(f, *varargs, axis=None, edge_order=1): if len_axes == 1: return outvals[0] - elif np._using_numpy2_behavior(): - return tuple(outvals) else: return outvals @@ -1951,7 +1949,11 @@ def place(arr, mask, vals): [44, 55, 44]]) """ - return _place(arr, mask, vals) + if not isinstance(arr, np.ndarray): + raise TypeError("argument 1 must be numpy.ndarray, " + "not {name}".format(name=type(arr).__name__)) + + return _insert(arr, mask, vals) def disp(mesg, device=None, linefeed=True): @@ -2117,10 +2119,10 @@ def _create_arrays(broadcast_shape, dim_sizes, list_of_core_dims, dtypes, @set_module('numpy') class vectorize: """ - vectorize(pyfunc, otypes=None, doc=None, excluded=None, cache=False, - signature=None) + vectorize(pyfunc=np._NoValue, otypes=None, doc=None, excluded=None, + cache=False, signature=None) - Generalized function class. + Returns an object that acts like pyfunc, but takes arrays as input. Define a vectorized function which takes a nested sequence of objects or numpy arrays as inputs and returns a single numpy array or a tuple of numpy @@ -2134,8 +2136,9 @@ class vectorize: Parameters ---------- - pyfunc : callable + pyfunc : callable, optional A python function or method. + Can be omitted to produce a decorator with keyword arguments. otypes : str or list of dtypes, optional The output data type. It must be specified as either a string of typecode characters or a list of data type specifiers. There should @@ -2167,8 +2170,9 @@ class vectorize: Returns ------- - vectorized : callable - Vectorized function. + out : callable + A vectorized function if ``pyfunc`` was provided, + a decorator otherwise. See Also -------- @@ -2265,18 +2269,44 @@ class vectorize: [0., 0., 1., 2., 1., 0.], [0., 0., 0., 1., 2., 1.]]) + Decorator syntax is supported. The decorator can be called as + a function to provide keyword arguments. + >>>@np.vectorize + ...def identity(x): + ... return x + ... + >>>identity([0, 1, 2]) + array([0, 1, 2]) + >>>@np.vectorize(otypes=[float]) + ...def as_float(x): + ... return x + ... + >>>as_float([0, 1, 2]) + array([0., 1., 2.]) """ - def __init__(self, pyfunc, otypes=None, doc=None, excluded=None, - cache=False, signature=None): + def __init__(self, pyfunc=np._NoValue, otypes=None, doc=None, + excluded=None, cache=False, signature=None): + + if (pyfunc != np._NoValue) and (not callable(pyfunc)): + #Splitting the error message to keep + #the length below 79 characters. + part1 = "When used as a decorator, " + part2 = "only accepts keyword arguments." + raise TypeError(part1 + part2) + self.pyfunc = pyfunc self.cache = cache self.signature = signature - self._ufunc = {} # Caching to improve default performance + if pyfunc != np._NoValue and hasattr(pyfunc, '__name__'): + self.__name__ = pyfunc.__name__ + self._ufunc = {} # Caching to improve default performance + self._doc = None + self.__doc__ = doc if doc is None: self.__doc__ = pyfunc.__doc__ else: - self.__doc__ = doc + self._doc = doc if isinstance(otypes, str): for char in otypes: @@ -2298,7 +2328,15 @@ class vectorize: else: self._in_and_out_core_dims = None - def __call__(self, *args, **kwargs): + def _init_stage_2(self, pyfunc, *args, **kwargs): + self.__name__ = pyfunc.__name__ + self.pyfunc = pyfunc + if self._doc is None: + self.__doc__ = pyfunc.__doc__ + else: + self.__doc__ = self._doc + + def _call_as_normal(self, *args, **kwargs): """ Return arrays with the results of `pyfunc` broadcast (vectorized) over `args` and `kwargs` not in `excluded`. @@ -2328,6 +2366,13 @@ class vectorize: return self._vectorize_call(func=func, args=vargs) + def __call__(self, *args, **kwargs): + if self.pyfunc is np._NoValue: + self._init_stage_2(*args, **kwargs) + return self + + return self._call_as_normal(*args, **kwargs) + def _get_ufunc_and_otypes(self, func, args): """Return (ufunc, otypes).""" # frompyfunc will fail if args is empty @@ -2693,7 +2738,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, if fact <= 0: warnings.warn("Degrees of freedom <= 0 for slice", - RuntimeWarning, stacklevel=2) + RuntimeWarning, stacklevel=3) fact = 0.0 X -= avg[:, None] @@ -2842,7 +2887,7 @@ def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue, *, if bias is not np._NoValue or ddof is not np._NoValue: # 2015-03-15, 1.10 warnings.warn('bias and ddof have no effect and are deprecated', - DeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=3) c = cov(x, y, rowvar, dtype=dtype) try: d = diag(c) @@ -3682,7 +3727,7 @@ def msort(a): warnings.warn( "msort is deprecated, use np.sort(a, axis=0) instead", DeprecationWarning, - stacklevel=2, + stacklevel=3, ) b = array(a, subok=True, copy=True) b.sort(0) @@ -4910,24 +4955,6 @@ def trapz(y, x=None, dx=1.0, axis=-1): return ret -# __array_function__ has no __code__ or other attributes normal Python funcs we -# wrap everything into a C callable. SciPy however, tries to "clone" `trapz` -# into a new Python function which requires `__code__` and a few other -# attributes. So we create a dummy clone and copy over its attributes allowing -# SciPy <= 1.10 to work: https://github.com/scipy/scipy/issues/17811 -assert not hasattr(trapz, "__code__") - -def _fake_trapz(y, x=None, dx=1.0, axis=-1): - return trapz(y, x=x, dx=dx, axis=axis) - - -trapz.__code__ = _fake_trapz.__code__ -trapz.__globals__ = _fake_trapz.__globals__ -trapz.__defaults__ = _fake_trapz.__defaults__ -trapz.__closure__ = _fake_trapz.__closure__ -trapz.__kwdefaults__ = _fake_trapz.__kwdefaults__ - - def _meshgrid_dispatcher(*xi, copy=None, sparse=None, indexing=None): return xi @@ -4936,7 +4963,7 @@ def _meshgrid_dispatcher(*xi, copy=None, sparse=None, indexing=None): @array_function_dispatch(_meshgrid_dispatcher) def meshgrid(*xi, copy=True, sparse=False, indexing='xy'): """ - Return a list of coordinate matrices from coordinate vectors. + Return coordinate matrices from coordinate vectors. Make N-D coordinate arrays for vectorized evaluations of N-D scalar/vector fields over N-D grids, given @@ -4977,7 +5004,7 @@ def meshgrid(*xi, copy=True, sparse=False, indexing='xy'): Returns ------- - X1, X2,..., XN : list of ndarrays + X1, X2,..., XN : ndarray For vectors `x1`, `x2`,..., `xn` with lengths ``Ni=len(xi)``, returns ``(N1, N2, N3,..., Nn)`` shaped arrays if indexing='ij' or ``(N2, N1, N3,..., Nn)`` shaped arrays if indexing='xy' @@ -5414,7 +5441,7 @@ def insert(arr, obj, values, axis=None): warnings.warn( "in the future insert will treat boolean arrays and " "array-likes as a boolean index instead of casting it to " - "integer", FutureWarning, stacklevel=2) + "integer", FutureWarning, stacklevel=3) indices = indices.astype(intp) # Code after warning period: #if obj.ndim != 1: |