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.

2 Answers 2

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']
Sign up to request clarification or add additional context in comments.

2 Comments

I see. I was trying my best to avoid using a for loop. Thanks, this answers my question.
FYI, @user2243024 the types of 'element-wise' functions (I used + as my example) that broadcast over an array are called ufuncs
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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.