The numpy.reshape (same as numpy.ndarray.reshape) method returns an array of the same total size, but in a new shape:
print(np.arange(10).reshape((2, 5)))
# [[0 1 2 3 4]
# [5 6 7 8 9]]
It returns a new array, and doesn't operate in place:
a = np.arange(12)
a.reshape((3, 4))
print(a)
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
However, it is possible to overwrite the shape attribute of an ndarray:
a = np.arange(12)
a.shape = (3, 4)
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
This behavior might be surprising at first, but ndarrays are stored in contiguous blocks of memory, and their shape only specifies how this stream of data should be interpreted as a multidimensional object.
Up to one axis in the shape tuple can have a value of -1. numpy will then infer the length of this axis for you:
a = np.arange(12)
print(a.reshape((3, -1)))
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
Or:
a = np.arange(12)
print(a.reshape((3, 2, -1)))
# [[[ 0 1]
# [ 2 3]]
# [[ 4 5]
# [ 6 7]]
# [[ 8 9]
# [10 11]]]
Multiple unspecified dimensions, e.g. a.reshape((3, -1, -1)) are not allowed and will throw a ValueError.