code: def _take_instances(self,
                          instances: Iterable[Instance],
                          max_instances: Optional[int] = None) -> Iterator[Instance]:
          
          # If max_instances isn't specified, just iterate once over the whole dataset
          if max_instances is None:
              yield from iter(instances)
          else:
              # If we don't have a cursor for this dataset, create one. We use ``id()``
              # for the key because ``instances`` could be a list, which can't be used as a key.
              key = id(instances)
              iterator = self._cursors.get(key, iter(instances))
  
              while max_instances > 0:
                  try:
                      # If there are instances left on this iterator,
                      # yield one and decrement max_instances.
                      yield next(iterator)
                      max_instances -= 1
                  except StopIteration:
                      # None left, so start over again at the beginning of the dataset.
                      iterator = iter(instances)
  
              # We may have a new iterator, so update the cursor.
              self._cursors[key] = iterator
docs: Take the next `max_instances` instances from the given dataset.
          If `max_instances` is `None`, then just take all instances from the dataset.
          If `max_instances` is not `None`, each call resumes where the previous one
          left off, and when you get to the end of the dataset you start again from the beginning.