Short answer:
Long answer:
Once you provide your own implementation of BreakfastArray.__array_finalize__, such as the following:
def __array_finalize__(self, obj):
print("finalize", f"self: {type(self)}", f"obj: {type(obj)}")
return super().__array_finalize__(obj)
… then you will realize that with your current code, it is only called twice, namely once for each instance creation (b1, b2) – but not for the concatenation.
What will be called, however, is __array_function__. We can directly follow the example from its documentation and adapt it to your needs:
import numpy as np
HANDLED_FUNCTIONS = {}
class BreakfastArray(np.ndarray):
def __new__(cls, n=1):
dtypes=[("waffles", int), ("eggs", int)]
obj = np.zeros(n, dtype=dtypes).view(cls)
return obj
def __array_function__(self, func, types, args, kwargs):
if func not in HANDLED_FUNCTIONS:
return NotImplemented
if not all(issubclass(t, BreakfastArray) for t in types):
return NotImplemented
return HANDLED_FUNCTIONS[func](*args, **kwargs)
def implements(numpy_function):
def decorator(func):
HANDLED_FUNCTIONS[numpy_function] = func
return func
return decorator
@implements(np.concatenate)
def concatenate(arrays):
result = BreakfastArray(n=sum(len(a) for a in arrays))
return np.concatenate([np.asarray(a) for a in arrays], out=result)
b1 = BreakfastArray(n=1)
b2 = BreakfastArray(n=2)
con_b1b2 = np.concatenate([b1, b2])
- Provide a dictionary
HANDLED_FUNCTIONS to hold custom implementations of Numpy functions for BreakfastArray.
- Provide a decorator
implements to store the implementations into the dictionary of custom implementations.
- Provide own implementation of
__array_function__ to access the dictionary of custom implementations.
- Provide own implementation of
concatenate to be stored in the dictionary of custom implementations. Note that here I call np.asarray() on the arrays to be concatenated, to avoid an infinite recursion into our own concatenate implementation; also, note that the current implementation does not support axis or out parameters.
If we only ever want to override concatenate, this can be simplified to:
import numpy as np
class BreakfastArray(np.ndarray):
def __new__(cls, n=1):
dtypes=[("waffles", int), ("eggs", int)]
obj = np.zeros(n, dtype=dtypes).view(cls)
return obj
def __array_function__(self, func, types, args, kwargs):
if func != np.concatenate or not all(issubclass(t, BreakfastArray) for t in types):
return NotImplemented
arrays = args[0]
result = BreakfastArray(n=sum(len(a) for a in arrays))
return np.concatenate([np.asarray(a) for a in arrays], out=result)
b1 = BreakfastArray(n=1)
b2 = BreakfastArray(n=2)
con_b1b2 = np.concatenate([b1, b2])