4

I am trying creating an NumPy array filled with an object, and I was wondering if there was a way I could broadcast to the entire array for each object to do something.

Code:

class player:
    def __init__(self,num = 5):
        self.num = num

    def printnum():
        print(self.num)
...

objs = np.array([player(5),player(6)],dtype=Object)
objs.printnum()

As it stands this returns an error. I have tried changing the dtype to: _object as per the manual, but nothing seems to work.

ali_m
  • 71,714
  • 23
  • 223
  • 298
user2243024
  • 55
  • 1
  • 4

2 Answers2

2

A numpy array of objects does not inherit the methods of that object. ndarray methods in general act on the entire array

This does not work for built-in types either, e.g.:

In [122]: import numpy as np

In [123]: n = 4.0

In [124]: a = np.arange(n)

In [125]: n.is_integer()
Out[125]: True

In [126]: a.is_integer()
---------------------------------------------------------------------------
AttributeError: 'numpy.ndarray' object has no attribute 'is_integer'

Numpy broadcasting is done with element-wise operators, for example addition:

In [127]: n
Out[127]: 4.0

In [128]: a
Out[128]: array([ 0.,  1.,  2.,  3.])

In [129]: n + a
Out[129]: array([ 4.,  5.,  6.,  7.])

If you want to basically call print on all the elements in your array, you could simply redefine the .__repr__() method which is called by print. I would caution you that you will lose information by overriding the method.

In [148]: class player:
   .....:     def __init__(self, num=5):
   .....:         self.num = num
   .....:     def __repr__(self):
   .....:         return str(self.num)
   .....:     

In [149]: objs = np.array([player(5), player(6)])

In [150]: objs
Out[150]: array([5, 6], dtype=object)

In [151]: print objs
[5 6]

Even though it looks like it, this is not the same as np.array([5,6]) though:

In [152]: objs * 3
----------------------
TypeError: unsupported operand type(s) for *: 'instance' and 'int'

And there you can see the disadvantage of overriding __repr__.

The simpler way to do this is to use your current printnum() method, but call it in a loop:

In [164]: class player:
   .....:     def __init__(self, num=5):
   .....:         self.num = num
   .....:     def printnum(self):
   .....:         print(self.num)
   .....:         

In [165]: for p in objs:
   .....:     p.printnum()
   .....:
5
6

Or, perhaps define your method to return a string rather than print one, then make a list comprehension:

In [169]: class player:
   .....:     def __init__(self, num=5):
   .....:         self.num = num
   .....:     def printnum(self):
   .....:         return str(self.num)
   .....: 

In [170]: objs = np.array([player(5), player(6)])

In [171]: [p.printnum() for p in objs]
Out[171]: ['5', '6']
askewchan
  • 45,161
  • 17
  • 118
  • 134
  • I see. I was trying my best to avoid using a for loop. Thanks, this answers my question. – user2243024 Apr 04 '13 at 16:25
  • FYI, @user2243024 the types of 'element-wise' functions (I used `+` as my example) that broadcast over an array are called [`ufunc`s](http://docs.scipy.org/doc/numpy/reference/ufuncs.html) – askewchan Apr 04 '13 at 18:50
1

Couple typos in your code: printnum() needs self arg, and Object->object

class player:
    def __init__(self, num=5):
        self.num = num

    def printnum(self):
        print(self.num)


objs = np.array([player(5),player(6)], dtype=object)

# It's not a "broadcast" (what you mean is map), but it has the same result
# plus it's pythonic (explicit + readable)
for o in objs:
    o.printnum()

It looks like what you really want to do is create a generator object. Google python generator yield and you'll get some examples like this

Community
  • 1
  • 1
hobs
  • 18,473
  • 10
  • 83
  • 106