You can add a new axis with None/np.newaxis at the start of the array to be appended : a[None,:,:] or simply a[None,...] or just a[None] and for stacking use np.vstack.
Here's a sample run to make things clear -
In [14]: c.shape
Out[14]: (2, 3, 4)
In [15]: d = np.vstack((c,a[None]))
In [16]: d.shape
Out[16]: (3, 3, 4)
In [17]: e = np.vstack((d,a[None]))
In [18]: e.shape
Out[18]: (4, 3, 4)
Workflow
So, the workflow would be :
1) To start off with 2D arrays, use new axes for the arrays :
c = np.vstack( (a[None],b[None]) )
2) For later appending steps, use new axis for the incoming 2D array and use np.vstack to stack with the existing 3D array -
d = np.vstack((c,a[None]))
Using np.concatenate for performance :
np.vstack under the hoods uses np.concatenate as a special case when we need to stack along the first axis. So, if we want to make use of np.concatenate maybe for performance reasons to avoid the additional function call overhead, we need to specify the axis of concatenation, which would be the first axis.
Thus, with np.concatenate -
In [23]: d = np.concatenate((c, a[None]), axis=0)
In [24]: d.shape
Out[24]: (3, 3, 4)
In [25]: e = np.concatenate((d, a[None]), axis=0)
In [26]: e.shape
Out[26]: (4, 3, 4)
stackcan work with a long list of 2d arrays like that. So if you have to do this repeatedly, append to the list andstackonce.cancatenateworks to add arrays along an existing dimension, but you'll have to add a dimension to the 2d to match those of the existing 3d.