I am diving into the SciPy LinAlg module for the first time, and I saw this function:
def _makearray(a):
    new = asarray(a)
    wrap = getattr(a, "__array_prepare__", new.__array_wrap__)
    return new, wrap
What does __array_wrap__ do exactly? I found the documentation, but I don't understand this explanation:
 At the end of every ufunc, this method is called on the input object with the
 highest array priority, or the output object if one was specified. The ufunc-
 computed array is passed in and whatever is returned is passed to the user. 
 Subclasses inherit a default implementation of this method, which transforms the
 array into a new instance of the object’s class. Subclasses may opt to use this
 method to transform the output array into an instance of the subclass and update
 metadata before returning the array to the user.
Does this just mean it reconverts the output of whatever function back into an array since it was likely broken up into something else for element-by-element processing? Relatedly, regardless of the explanation, what would it mean to get this wrap as an object? What would you do with it?
I am looking at the code for numpy.linalg.inv...what is wrap doing here?
    **a, wrap = _makearray(a)**
    _assertRankAtLeast2(a)
    _assertNdSquareness(a)
    t, result_t = _commonType(a)
    if a.shape[-1] == 0:
        # The inner array is 0x0, the ufunc cannot handle this case
        **return wrap(empty_like(a, dtype=result_t))**
    signature = 'D->D' if isComplexType(t) else 'd->d'
    extobj = get_linalg_error_extobj(_raise_linalgerror_singular)
    ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
    return wrap(ainv.astype(result_t))
 
    