On Thu, May 30, 2013 at 4:58 PM, Aldcroft, Thomas < [email protected]> wrote:
> > > > On Thu, May 30, 2013 at 4:27 PM, Robert Kern <[email protected]>wrote: > >> On Thu, May 30, 2013 at 9:21 PM, Aldcroft, Thomas >> <[email protected]> wrote: >> > I'm seeing some behavior that I can't understand when creating a numpy >> array >> > of Python objects. Basically it seems that np.array() is calling the >> object >> > __getitem__ method for one object class but not another class, and I >> can't >> > understand the difference. >> > >> > Here is an example, starting with a simple class where __getitem__ does >> NOT >> > get called: >> > >> >>>> class Foo(object): >> > ... def __getitem__(self, item): >> > ... return 1/0 >> > ... def __len__(self, item): >> > ... return 1 >> > ... >> >>>> f = Foo() >> >>>> x = np.array([f]) >> >>>> x >> > array([<__main__.Foo object at 0x11a1ff10>], dtype=object) >> > >> > Now move to the complicated class (astropy.time.Time) which has a lot of >> > stuff in it (including __new__, __init__, and __len__ methods, but >> initially >> > no __getitem__), but is otherwise an ordinary class derived from object. >> > This works as expected: >> > >> >>>> from astropy.time import Time >> >>>> t = Time('2001-01-01', scale='utc') >> >>>> x = np.array([t]) >> >>>> x >> > array([2001-01-01 00:00:00.000], dtype=object) >> > >> > Now inject a __getitem__ that will fail and try again: >> > >> >>>> Time.__getitem__ = lambda self, item: 1 / 0 >> >>>> x = np.array([t]) >> > ERROR: ZeroDivisionError: integer division or modulo by zero >> > >> > Any ideas on what is driving this difference in behavior? BTW, the >> value of >> > item in the latter case is 0. >> >> What is len(t) in the latter case? >> > > >>> len(t) > 1 > > Prompted by this question I looked again at the Foo() definition and see > that I had copy/pasted the definition of __len__ from __getitem__ and > forgot to remove the `item` arg. As written `len(Foo())` would fail. > Interestingly, once `Foo.__len__` is valid and succeeds, then > `f.__getitem__` does indeed get called and `np.array([f])` fails in the > same way. > > Sorry for the noise, though this is still just slightly curious to me. I > guess internally maybe there is try/except block that is trying to get a > len and if that fails then it moves on. > Now I realize what seemed curious. Here is a related example which shows that when initializing a numpy array of objects where __getitem__ and __len__ exist, np.array introspects the object item values for item in range(len(object)) and appears to replace the input object with an ndarray of the object values. >>> class Foo(object): ... attr = 'hello' ... def __getitem__(self, item): ... return item ... def __len__(self): ... return 5 >>> f = Foo() >>> f.attr 'hello' >>> x = np.array([f, f], dtype=object) >>> x array([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], dtype=object) >>> x[0] array([0, 1, 2, 3, 4], dtype=object) >>> type(x[0]) <type 'numpy.ndarray'> >>> x[0].attr Traceback (most recent call last): File "<ipython-input-16-7b865674a0b6>", line 1, in <module> x[0].attr AttributeError: 'numpy.ndarray' object has no attribute 'attr' The actual objects that I passed in seem to be lost, which is not the behavior I expected. Thanks, Tom > - Tom > > > >> >> -- >> Robert Kern >> _______________________________________________ >> NumPy-Discussion mailing list >> [email protected] >> http://mail.scipy.org/mailman/listinfo/numpy-discussion >> > >
_______________________________________________ NumPy-Discussion mailing list [email protected] http://mail.scipy.org/mailman/listinfo/numpy-discussion
