code: def batch_iter(iterator, batch_size, return_func=None, padding=None):
      
      for batch in zip_longest(*[iter(iterator)]*batch_size, fillvalue=padding):
          gen = (thing for thing in batch if thing is not padding)
          if return_func is None:
              yield gen
          else:
              yield return_func(gen)
docs: Break an iterable into batches of size batch_size
  
      Note that `padding` should be set to something (anything) which is NOT a
      valid member of the iterator. For example, None works for [0,1,2,...10], but
      not for ['a', None, 'c', 'd'].
  
      Parameters
      ----------
      iterator : iterable
          A python object which is iterable.
      batch_size : int
          The size of batches you wish to produce from the iterator.
      return_func : executable or None
          Pass a function that takes a generator and returns an iterable (e.g.
          `list` or `set`). If None, a generator will be returned.
      padding : anything
          This is used internally to ensure that the remainder of the list is
          included. This MUST NOT be a valid element of the iterator.
  
      Returns
      -------
      An iterator over lists or generators, depending on `return_lists`.