Define a list containing 3 different arrays:
In [241]: ll = [np.ones((3,4),int), np.zeros((2,4),bool), np.arange(6).reshape(2,3)]
In [242]: ll
Out[242]:
[array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]),
array([[False, False, False, False],
[False, False, False, False]], dtype=bool),
array([[0, 1, 2],
[3, 4, 5]])]
I could wrap that list in array:
In [243]: np.array(ll)
Out[243]:
array([array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]),
array([[False, False, False, False],
[False, False, False, False]], dtype=bool),
array([[0, 1, 2],
[3, 4, 5]])], dtype=object)
Notice the dtype of the outer array is object. In other words, the array is shape (3,), and contains pointers to objects elsewhere. I could replace any of those arrays with other python objects - None, a list, a number, or a dictionary.
Don't go this route unless you really need an object type array. For most purposes the list is just as good if not better. Lists are the original Python structure for holding a diverse collection of objects. Arrays are designed to hold a fixed number of homogeneous objects such as numbers. It's bad practice to start with a small empty array and try to grow it.