def blockgen(array, bpa):
    """Creates a generator that yields multidimensional blocks from the given
array(_like); bpa is an array_like consisting of the number of blocks per axis
(minimum of 1, must be a divisor of the corresponding axis size of array). As
the blocks are selected using normal numpy slicing, they will be views rather
than copies; this is good for very large multidimensional arrays that are being
blocked, and for very large blocks, but it also means that the result must be
copied if it is to be modified (unless modifying the original data as well is
intended)."""
bpa = np.asarray(bpa)
if array.ndim != bpa.size:
    raise ValueError('Size of bpa must be equal to the array dimensionality.')
if bpa.dtype != np.int or (bpa < 1).any() or (array.shape % bpa).any():
    raise ValueError(
        'bpa ({0}) must consist of nonzero positive integers that evenly divide the corresponding array axis size'
        .format(bpa))
rgen = (np.r_[:array.shape[i] + 1:array.shape[i] // blk_n] for i, blk_n in
    enumerate(bpa))
c = [[np.s_[i:j] for i, j in zip(r[:-1], r[1:])] for r in rgen]
for idxs in np.ndindex(*bpa):
    blockbounds = tuple(c[j][idxs[j]] for j in range(bpa.size))
yield array[blockbounds]
Slice 2d array into smaller 2d arrays