
Hi Stephan, Eric perhaps explained my concept better than I could! I do agree that, as written, your example would be clearer, but Allan's code and the current MaskedArray code do have not that much semblance to it, and mine even less, as they deal with operators as whole groups. For mine, it may be useful to tell that this quite possibly crazy idea came from considering how one would quite logically implement all shape operations, which effect the data and mask the same way, so one soon writes down something where (as in my ShapedLikeNDArray mixin; https://github.com/astropy/astropy/blob/master/astropy/utils/misc.py#L858) all methods pass on to `return self._apply(method, <arguments>), with `_apply` looking like: ``` def _apply(self, method, *args, **kwargs): # For use with NDArrayShapeMethods. data = getattr(self._data, method)(*args, **kwargs) mask = getattr(self._mask, method)(*args, **kwargs) return self.__class__(data, mask=mask) ``` (Or the same is auto-generated for all those methods.) Now considering this, for `__array_ufunc__`, one might similarly do (__call__ only, ignoring outputs) ``` def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): data = [] masks = [] for input_ in inputs: d, m = self._data_mask(input_) data.append(d) masks.append(m) result_mask = getattr(ufunc, method)(*masks, **mask_kwargs) if result_mask is NotImplemented: return NotImplemented result_data = getattr(ufunc, method)(*data, **kwargs) return self._masked_result(result_data, result_mask, out) ``` The beauty here is that the Masked class itself needs to know very little about how to do masking, or how to implement it; it is almost like a collection of arrays: the only thing that separates `__array_ufunc__` from `_apply` above is that, since other objects are involved, the data and mask have to be separated out for those. (And perhaps for people trying to change it, it actually helps that the special-casing of single-, double-, and triple-input ufuncs can all be done inside the mask class, and adjustments can be made there without having to understand the machinery for getting masks out of data, etc.?) But what brought me to this was not __array_ufunc__ itself, but rather the next step, that I really would like to have masked version of classes that are array-like in shape but do not generally work with numpy ufuncs or functions, and where I prefer not to force the use of numpy ufuncs. So, inside the masked class I have to override the operators. Obviously, there again I could do it with functions like you describe, but I can also just split off data and masks as above, and just call the same operator on both. Then, the code could basically use that of `__array_ufunc__` above when called as `self.__array_ufunc__(operator, '__add__', self, other)`. I think (but am not sure, not actually having tried it yet), that this would also make it easier to mostly auto-generated masked classes: those supported by both the mask and the data are relatively easy; those separate for the data need some hints from the data class (e.g., `.unit` on a `Quantity` is independent of the mask). Anyway, just to be clear, I am *not* sure that this is the right way forward. I think the more important really was your suggestion to take mask duck types into account a priori (for bit-packed ones, etc.). But it seems worth thinking it through now, so we don't repeat the things that ended up making `MaskedArray` quite annoying. All the best, Marten