[Numpy-discussion] metadata and metabehavior for arrays (for scipy.base or Numeric3)

David M. Cooke cookedm at physics.mcmaster.ca
Wed Apr 6 15:44:36 EDT 2005

konrad.hinsen at laposte.net writes:

> On Apr 6, 2005, at 12:10, Sébastien de Menten wrote:
>> However, I disagree with the "pretty straightforward to
>> implement". In fact, if one wants to inherit most of the
>> functionalities of Numeric, it becomes quite cumbersome. Looking at
>> MA module, I see that it needs to:
> It is straightforward AND cumbersome. Lots of work, but nothing
> difficult. I agree of course that it would be nice to improve the
> situation.
>> An embryo of idea would be to add hooks in the machinery to allow an
>> object to interact with an ufunc. Currently, this is done by calling
>> __array__ to extract a "naked array" (== Numeric.array vs
>> "augmented array") but the result is then always a "naked
>> array".
>> In pseudocode, this looks like:
>>  def ufunc( augmented_array ):
>>    if not isarray(augmented_array):
>>      augmented_array = augmented_array.__array__()
>>    return ufunc.apply(augmented_array)
> The current behaviour of Numeric is more like
> 	def ufunc(object):
> 		if isarray(object):
> 			return array_ufunc(object)
> 		elif is_array_like(object):
> 			return array_func(array(object))
> 		else:
> 			return object.ufunc()
> A more general version, which should cover your case as well, would be:
> 	def ufunc(object):
> 		if isarray(object):
> 			return array_ufunc(object)
> 		else:
> 			try:
> 				return object.applyUfunc(ufunc)
> 			except AttributeError:
> 				if is_array_like(object):
> 					return array_func(array(object))
> 				else:
> 					raise ValueError
> There are two advantages:
> 1) Classes can handle ufuncs in any way they like, even if they
> implement
>     array-like objects.
> 2) Classes must implement only one method, not one per ufunc.

I like this! It's got namespace goodness all over it (last Python zen
line in 'import this': Namespaces are one honking great idea -- let's
do more of those!)

I'd propose making the special method __ufunc__.

> Compared to the approach that you suggested:
>> where I would prefer something like
>>  def ufunc( augmented_array ):
>>    if not isarray(augmented_array):
>>      augmented_array, contructor =
>> augmented_array.__array_constructor__()
>>    else:
>>      constructor = lambda x:x
>>    return constructor(ufunc.apply(augmented_array))
> mine has the advantage of also covering classes that are not
> array-like at all.

... like your derivative classes, which are very useful. There are two
different uses that ufuncs apply to, however.

1) arrays. Here, we want efficient computation of functions applied to
   lots of elements. That's where the output arguments and special
   methods (.reduce, .accumulate, and .outer) are useful
2) polymorphic functions. Output arguments aren't useful here. The
   special methods are useful for binary ufuncs only.

For #2, just returning a callable from __ufunc__ would be fine. I'd
suggest two levels of an informal ufunc interface corresponding to
these two uses.

|David M. Cooke                      http://arbutus.physics.mcmaster.ca/dmc/
|cookedm at physics.mcmaster.ca

More information about the NumPy-Discussion mailing list