class Foo:
def __init__(self, y, x):
self.y = y
self.x = x
def __repr__(self):
return f'Foo({self.y}, {self.x})'
In [69]: Foo(1,2)
Out[69]: Foo(1, 2)
The proposed nditer solution:
def foo1(n):
a = np.empty((n,n), dtype=object)
with np.nditer(a, flags=['multi_index', 'refs_ok'], op_flags=['writeonly']) as it:
for x in it:
x[...] = Foo(it.multi_index[1], it.multi_index[0])
return a
In [70]: foo1(2)
Out[70]:
array([[Foo(0, 0), Foo(1, 0)],
[Foo(0, 1), Foo(1, 1)]], dtype=object)
The nested loop:
def foo2(n):
a = np.empty((n,n), dtype=object)
for i in range(n):
for j in range(n):
a[i,j] = Foo(i,j)
return a
In [71]: foo2(2)
Out[71]:
array([[Foo(0, 0), Foo(0, 1)],
[Foo(1, 0), Foo(1, 1)]], dtype=object)
my favorite, frompyfunc:
def foo3(n):
f = np.frompyfunc(Foo, 2, 1)
I,J = np.meshgrid(np.arange(n),np.arange(n), sparse=True)
return f(I,J)
In [72]: foo3(2)
Out[72]:
array([[Foo(0, 0), Foo(1, 0)],
[Foo(0, 1), Foo(1, 1)]], dtype=object)
timings:
In [73]: timeit foo1(200)
144 ms ± 305 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [74]: timeit foo2(200)
25.7 ms ± 958 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [75]: timeit foo3(200)
17.7 ms ± 40.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
And for comparison, a nested list:
def foo4(n):
alist = []
for i in range(n):
blist = []
for j in range(n):
blist.append(Foo(i,j))
alist.append(blist)
return alist
In [77]: foo4(2)
Out[77]: [[Foo(0, 0), Foo(0, 1)], [Foo(1, 0), Foo(1, 1)]]
In [78]: timeit foo4(200)
18.6 ms ± 149 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
numpy.ndarray. If your class is actually something like this, i.e. basic container of attributes, then maybe you could use a structured array instead and keep the benefits of numpynumpy.fromiterand an iterator returningFooinstances might work. It will make an 1-D array but you can reshape it afterwards. However, it might be a bit slow anyways, and use a lot of memory. Are you sure that you actually need 40,000 instances of a class?