code: def batch_generator(data, batch_size=32, epochs=None, shuffle=True):
      """
      Iterates over the data for the given number of epochs, yielding batches of
      size `batch_size`.
      :param data: np.array or list of np.arrays with the same first dimension;
      :param batch_size: number of samples in a batch;
      :param epochs: number of times to iterate over the data (default None, iterates
      indefinitely);
      :param shuffle: whether to shuffle the data at the beginning of each epoch
      :return: batches of size `batch_size`.
      """
      if not isinstance(data, (list, tuple)):
          data = [data]
      if len(data) < 1:
          raise ValueError("data cannot be empty")
      if len({len(item) for item in data}) > 1:
          raise ValueError("All inputs must have the same __len__")
  
      if epochs is None or epochs == -1:
          epochs = np.inf
      len_data = len(data[0])
      batches_per_epoch = int(np.ceil(len_data / batch_size))
      epoch = 0
      while epoch < epochs:
          epoch += 1
          if shuffle:
              shuffle_inplace(*data)
          for batch in range(batches_per_epoch):
              start = batch * batch_size
              stop = min(start + batch_size, len_data)
              to_yield = [item[start:stop] for item in data]
              if len(data) == 1:
                  to_yield = to_yield[0]
  
              yield to_yield
  
file_path: spektral/spektral/data/utils.py
imports: import numpy
  import tensorflow
  from scipy import sparse
  from spektral.utils import pad_jagged_array
  from spektral.utils.sparse import sp_matrix_to_sp_tensor
label_desc: Use image node_id 4 for calling a global function with example usage: batch_generator(data, batch_size, epochs, shuffle) without return types