To get the basic idea, you can change your function, for example, like this:
def func2(x, y): x, y= x[:, None], y[None, :] A= x+ y A[x<= y]= (x- y)[x<= y] return A
So in your case, something like this should be a very reasonable starting point:
In []: def func(x, y): ..: x, y= x[:, None], y[None, :] ..: return x+ y ..: In []: def func2(x, y): ..: x, y= x[:, None], y[None, :] ..: A, L= x+ y, x<= y ..: A[L]= (x- y)[L] ..: return A ..: In []: x, y= arange(-2, 3), arange(-2, 3) In []: func(x, y) Out[]: array([[-4, -3, -2, -1, 0], [-3, -2, -1, 0, 1], [-2, -1, 0, 1, 2], [-1, 0, 1, 2, 3], [ 0, 1, 2, 3, 4]]) In []: func2(x, y) Out[]: array([[ 0, -1, -2, -3, -4], [-3, 0, -1, -2, -3], [-2, -1, 0, -1, -2], [-1, 0, 1, 0, -1], [ 0, 1, 2, 3, 0]])
Although this type of processing may seem unnecessary, it is not necessary. Always measure the actual performance of your programs and make the necessary (not earlier) necessary changes.
IMHO for added value: this type of "vectorization" makes your code truly consistent and readable in the long run.