[PYTHON MATRIX-SIG] changes to fix __array__()

Chris Chase SRM chris.chase@jhuapl.edu
Fri, 29 Nov 1996 15:38:39 -0500


I am using NumPy-1.0a5.tar and Python 1.4.

I was trying to use the __array__(self, typecode=None) interface for
getting an object as an array and found that it did not work.  I
traced the problem to array_fromobject() in arrayobject.c.

I have made the necessary changes to array_fromobject() in
arrayobject.c.  I also made corrections to refecrence handling of
Python objects in the same function (according to Lutz's PP book both
PyEval_CallObject() and PyObject_GetAttrString() pass pass owenership
of result, i.e. it is already INCREF'd and must be DECREF'd when
finished).

As a result of all this I found that asarray() did not use
PyArray_FromObject() but instead copies via PyArray_CopyFromObject
when using the __array__() interface.  To make it use
PyArray_FromObject(), I moved asarray() in multiarray.c instead of in
Numeric.py.


An example of the fixed behavior of the __array__ interface and
asarray(): 

class a:
	def __init__(self):
 		self.a = arange(8)
 	def __array__(self,t=None):
		if t: return asarray(self.a,t)
		return asarray(self.a)

b = a()
c = array(b)
c[2] = -9
>>> c
 0  1 -9  3  4  5  6  7
>>> b.a
0 1 2 3 4 5 6 7

d = asarray(b)
d[2] = -9
>>> b.a
 0  1 -9  3  4  5  6  7

According to the documentation for array the interface is given as
"array(sequence, typecode=None)" which seems to indicate that None is
valid value for typecode.  However, None is not a valid value and
typecode is not a keyword argument.  The documentation should be
changed.  Although, I think that it would be better to let typecode be
a possible keyword argument and let None be a valid value that gets
mapped to PyArray_NOTYPE (in arrayobject.h).

Anyway, for the __array__ and asarray changes I modified
arrayobject.c, multiarray.c and Numeric.py.  The context diffs created
in the NumPy directory are below, which I believe are suitable for the
patch program, are below.

Chris Chase


===================================================================
RCS file: arrayobject.c,v
retrieving revision 1.1
diff -c5 -r1.1 arrayobject.c
*** arrayobject.c	1996/11/28 07:10:25	1.1
--- arrayobject.c	1996/11/29 19:13:11
***************
*** 1612,1641 ****
  }
  
  #define ByCopy 1
  #define Contiguous 2
  
! PyObject *array_fromobject(PyObject *op, int type, int min_depth, int max_depth, int flags) {
! 	PyObject *r;
! 	
  	r = NULL;
  	
! 	if (!PyArray_Check(op) && PyObject_HasAttrString(op, "__array__")) {
  		PyObject *ap, *arglist;
  		
  		if (type == PyArray_NOTYPE) {
  			arglist = Py_BuildValue("()");
  		} else {
  			arglist = Py_BuildValue("(c)", type);
  		}
! 		ap = PyObject_GetAttrString(op, "__array__");
! 		r = PyEval_CallObject(ap, arglist);
  		Py_DECREF(arglist);
! 		
! 		Py_XINCREF(r);
! 	}
! 	
  	if (type == PyArray_NOTYPE) {
  		type = PyArray_ObjectType(op, 0);
  	}
  	
  	if (PyArray_Check(op) && (((PyArrayObject *)op)->descr->type_num != PyArray_OBJECT || 
--- 1612,1646 ----
  }
  
  #define ByCopy 1
  #define Contiguous 2
  
! PyObject *array_fromobject(PyObject *op_in, int type, int min_depth, int max_depth, int flags) {
! 	PyObject *r, *op;
!     
  	r = NULL;
  	
! 	if (!PyArray_Check(op_in) && PyObject_HasAttrString(op_in, "__array__")) {
!         /* __array__(self, type=None) method interface
!            for getting an object as an array. */
  		PyObject *ap, *arglist;
  		
  		if (type == PyArray_NOTYPE) {
  			arglist = Py_BuildValue("()");
  		} else {
  			arglist = Py_BuildValue("(c)", type);
  		}
! 		ap = PyObject_GetAttrString(op_in, "__array__");
! 		op = PyEval_CallObject(ap, arglist);
!         Py_DECREF(ap);
  		Py_DECREF(arglist);
! 		if (op == NULL) return NULL;
! 	} else {
!         op = op_in;
!         Py_INCREF(op);
!     }
! 
  	if (type == PyArray_NOTYPE) {
  		type = PyArray_ObjectType(op, 0);
  	}
  	
  	if (PyArray_Check(op) && (((PyArrayObject *)op)->descr->type_num != PyArray_OBJECT || 
***************
*** 1648,1673 ****
  				Py_INCREF(op);
  				r = op;
  			}
  		} else {
  			if (type > PyArray_NTYPES) type = PyArray_DescrFromType(type)->type_num;
! 			if ((r = PyArray_Cast((PyArrayObject *)op, type)) == NULL) return NULL;
  		}
  	} else {
  		r = Array_FromSequence(op, type, min_depth,max_depth);
! 		if (r == NULL) {
! 			if (min_depth <= 0) {
! 				PyErr_Clear();
! 				r = PyArray_FromScalar(op, type);
! 			} else {
! 				return NULL;
! 			}
  		}
  	}
  	if (r == NULL) return NULL;
  	
! 	if(!PyArray_Check(r)) {
  		PyErr_SetString(PyExc_ValueError, "Internal error array_fromobject not producing an array");
  		return NULL;
  	}
  	if (min_depth != 0 && ((PyArrayObject *)r)->nd < min_depth) {
  		Py_DECREF(r);
--- 1653,1677 ----
  				Py_INCREF(op);
  				r = op;
  			}
  		} else {
  			if (type > PyArray_NTYPES) type = PyArray_DescrFromType(type)->type_num;
! 			r = PyArray_Cast((PyArrayObject *)op, type);
  		}
  	} else {
  		r = Array_FromSequence(op, type, min_depth,max_depth);
! 		if (r == NULL && min_depth <= 0) {
!             PyErr_Clear();
!             r = PyArray_FromScalar(op, type);
  		}
  	}
+     /* finished with op */
+     Py_DECREF(op);
+     
  	if (r == NULL) return NULL;
  	
! 	if (!PyArray_Check(r)) {
  		PyErr_SetString(PyExc_ValueError, "Internal error array_fromobject not producing an array");
  		return NULL;
  	}
  	if (min_depth != 0 && ((PyArrayObject *)r)->nd < min_depth) {
  		Py_DECREF(r);
rcsdiff: RCS/multiarray.c,v: No such file or directory
rcsdiff: Numeric/RCS/Numeric.py,v: No such file or directory
custer [121]> ../misc/makepatch 
../misc/makepatch 
===================================================================
RCS file: arrayobject.c,v
retrieving revision 1.1
diff -c5 -r1.1 arrayobject.c
*** arrayobject.c	1996/11/28 07:10:25	1.1
--- arrayobject.c	1996/11/29 19:13:11
***************
*** 1612,1641 ****
  }
  
  #define ByCopy 1
  #define Contiguous 2
  
! PyObject *array_fromobject(PyObject *op, int type, int min_depth, int max_depth, int flags) {
! 	PyObject *r;
! 	
  	r = NULL;
  	
! 	if (!PyArray_Check(op) && PyObject_HasAttrString(op, "__array__")) {
  		PyObject *ap, *arglist;
  		
  		if (type == PyArray_NOTYPE) {
  			arglist = Py_BuildValue("()");
  		} else {
  			arglist = Py_BuildValue("(c)", type);
  		}
! 		ap = PyObject_GetAttrString(op, "__array__");
! 		r = PyEval_CallObject(ap, arglist);
  		Py_DECREF(arglist);
! 		
! 		Py_XINCREF(r);
! 	}
! 	
  	if (type == PyArray_NOTYPE) {
  		type = PyArray_ObjectType(op, 0);
  	}
  	
  	if (PyArray_Check(op) && (((PyArrayObject *)op)->descr->type_num != PyArray_OBJECT || 
--- 1612,1646 ----
  }
  
  #define ByCopy 1
  #define Contiguous 2
  
! PyObject *array_fromobject(PyObject *op_in, int type, int min_depth, int max_depth, int flags) {
! 	PyObject *r, *op;
!     
  	r = NULL;
  	
! 	if (!PyArray_Check(op_in) && PyObject_HasAttrString(op_in, "__array__")) {
!         /* __array__(self, type=None) method interface
!            for getting an object as an array. */
  		PyObject *ap, *arglist;
  		
  		if (type == PyArray_NOTYPE) {
  			arglist = Py_BuildValue("()");
  		} else {
  			arglist = Py_BuildValue("(c)", type);
  		}
! 		ap = PyObject_GetAttrString(op_in, "__array__");
! 		op = PyEval_CallObject(ap, arglist);
!         Py_DECREF(ap);
  		Py_DECREF(arglist);
! 		if (op == NULL) return NULL;
! 	} else {
!         op = op_in;
!         Py_INCREF(op);
!     }
! 
  	if (type == PyArray_NOTYPE) {
  		type = PyArray_ObjectType(op, 0);
  	}
  	
  	if (PyArray_Check(op) && (((PyArrayObject *)op)->descr->type_num != PyArray_OBJECT || 
***************
*** 1648,1673 ****
  				Py_INCREF(op);
  				r = op;
  			}
  		} else {
  			if (type > PyArray_NTYPES) type = PyArray_DescrFromType(type)->type_num;
! 			if ((r = PyArray_Cast((PyArrayObject *)op, type)) == NULL) return NULL;
  		}
  	} else {
  		r = Array_FromSequence(op, type, min_depth,max_depth);
! 		if (r == NULL) {
! 			if (min_depth <= 0) {
! 				PyErr_Clear();
! 				r = PyArray_FromScalar(op, type);
! 			} else {
! 				return NULL;
! 			}
  		}
  	}
  	if (r == NULL) return NULL;
  	
! 	if(!PyArray_Check(r)) {
  		PyErr_SetString(PyExc_ValueError, "Internal error array_fromobject not producing an array");
  		return NULL;
  	}
  	if (min_depth != 0 && ((PyArrayObject *)r)->nd < min_depth) {
  		Py_DECREF(r);
--- 1653,1677 ----
  				Py_INCREF(op);
  				r = op;
  			}
  		} else {
  			if (type > PyArray_NTYPES) type = PyArray_DescrFromType(type)->type_num;
! 			r = PyArray_Cast((PyArrayObject *)op, type);
  		}
  	} else {
  		r = Array_FromSequence(op, type, min_depth,max_depth);
! 		if (r == NULL && min_depth <= 0) {
!             PyErr_Clear();
!             r = PyArray_FromScalar(op, type);
  		}
  	}
+     /* finished with op */
+     Py_DECREF(op);
+     
  	if (r == NULL) return NULL;
  	
! 	if (!PyArray_Check(r)) {
  		PyErr_SetString(PyExc_ValueError, "Internal error array_fromobject not producing an array");
  		return NULL;
  	}
  	if (min_depth != 0 && ((PyArrayObject *)r)->nd < min_depth) {
  		Py_DECREF(r);
rcsdiff: RCS/multiarray.c,v: No such file or directory
===================================================================
RCS file: Numeric/Numeric.py,v
retrieving revision 1.1
diff -c5 -r1.1 Numeric/Numeric.py
*** Numeric/Numeric.py	1996/11/29 20:24:40	1.1
--- Numeric/Numeric.py	1996/11/29 20:25:12
***************
*** 31,40 ****
--- 31,41 ----
  		return m.astype(typecode)
  	else:
  		return m
  
  #Include some functions straight from multiarray
+ asarray = multiarray.asarray
  array = multiarray.array
  zeros = multiarray.zeros
  fromstring = multiarray.fromstring
  take = multiarray.take
  reshape = multiarray.reshape
***************
*** 235,251 ****
  def nonzero(a):
  	"""Return the indices of the elements of a which are not zero, a must be 1d
  	"""
  	return repeat(arange(len(a)), not_equal(a, 0))
  
! def asarray(a, typecode=None):
! 	if type(a) == arraytype and (typecode == None or typecode == a.typecode()): 
! 		return a
! 	elif typecode == None:
! 		return array(a)
! 	else:
! 		return array(a, typecode)
  
  #Move this into C to do it right!
  def shape(a):
  	return asarray(a).shape
  
--- 236,252 ----
  def nonzero(a):
  	"""Return the indices of the elements of a which are not zero, a must be 1d
  	"""
  	return repeat(arange(len(a)), not_equal(a, 0))
  
! ## def asarray(a, typecode=None):
! ## 	if type(a) == arraytype and (typecode == None or typecode == a.typecode()): 
! ## 		return a
! ## 	elif typecode == None:
! ## 		return array(a)
! ## 	else:
! ## 		return array(a, typecode)
  
  #Move this into C to do it right!
  def shape(a):
  	return asarray(a).shape
  

=================
MATRIX-SIG  - SIG on Matrix Math for Python

send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================