//  [python]
def __call__(self, params):
    batch_size = params['batch_size']
    dataset = tf.data.Dataset.list_files(self._file_pattern, shuffle=False)

    if self._is_training or self._distributed_eval:
      dataset = dataset.shard(params['dataset_num_shards'],
                              params['dataset_index'])
      if self._is_training:
        dataset = dataset.shuffle(
            tf.cast(
                max(256, params['dataset_num_shards']) /
                params['dataset_num_shards'], tf.int64))

    # Prefetch data from files.
    dataset = dataset.interleave(tf.data.TFRecordDataset,
                                 FLAGS.read_parallelism or None, 1,
                                 FLAGS.read_parallelism or None)

    if FLAGS.use_cache:
        dataset = dataset.take(1000).cache().repeat()

    # Parse the fetched records to input tensors for model function.
    dataset = dataset.map(self._decode,
                          num_parallel_calls=FLAGS.map_tfrecord_decode_parallelism or None)

    def _mark_is_padded(data):
      sample = data
      sample[ssd_constants.IS_PADDED] = tf.constant(True, dtype=tf.bool)
      return sample

    def _mark_is_not_padded(data):
      sample = data
      sample[ssd_constants.IS_PADDED] = tf.constant(False, dtype=tf.bool)
      return sample

    # Pad dataset to the desired size and mark if the data is padded.
    # During eval/predict, if local_batch_size * num_shards > 5000,
    # original dataset size won't be fit for computations on that number
    # of shards. In this case, will take
    # (local_batch_size - 5000 / num_shards) data from the original dataset
    # on each shard and mark the padded data as `is_padded`.
    # Also mark the original data as `not_padded`.
    # Append the padded data to the original dataset.
    if not self._is_training and self._count > self._params['eval_samples']:
      padded_dataset = dataset.map(_mark_is_padded)
      dataset = dataset.map(_mark_is_not_padded)
      dataset = dataset.concatenate(padded_dataset).take(
          self._count // params['dataset_num_shards'])

    if self._is_training:
      dataset = dataset.filter(
          lambda data: tf.greater(tf.shape(data['groundtruth_boxes'])[0], 0))
      # TODO(mkuchnik): We disable ad-hoc caches
      #if not self._use_fake_data:
      #  dataset = dataset.cache().shuffle(64).repeat()
    else:
      dataset = dataset.prefetch(batch_size * 64)

    dataset = dataset.map(self._parse_example,
                          num_parallel_calls=FLAGS.map_parse_parallelism or None)
    dataset = dataset.batch(batch_size=batch_size, drop_remainder=True)

    if self._params['conv0_space_to_depth']:

      def _space_to_depth_fn(images, labels):
        images = fused_transpose_and_space_to_depth(
            images,
            block_size=ssd_constants.SPACE_TO_DEPTH_BLOCK_SIZE,
            transpose_input=self._transpose_input)
        if self._transpose_input:
          labels[ssd_constants.BOXES] = tf.transpose(
              labels[ssd_constants.BOXES], [1, 2, 0])
        return images, labels

      dataset = dataset.map(_space_to_depth_fn, num_parallel_calls=FLAGS.map_image_postprocessing_parallelism or None)
    elif self._transpose_input:
      # Manually apply the double transpose trick for training data.
      def _transpose_dataset(image, labels):
        if batch_size > 8:
          image = tf.transpose(image, [1, 2, 3, 0])
        else:
          image = tf.transpose(image, [1, 2, 0, 3])
        labels[ssd_constants.BOXES] = tf.transpose(labels[ssd_constants.BOXES],
                                                   [1, 2, 0])
        return image, labels

      dataset = dataset.map(_transpose_dataset,
                            num_parallel_calls=FLAGS.map_image_transpose_postprocessing_parallelism or None)

    dataset = dataset.prefetch(64)
    options = tf.data.Options()
    options.experimental_threading.max_intra_op_parallelism = 1
    options.experimental_threading.private_threadpool_size = self._params[
        'dataset_threadpool_size']
    if FLAGS.map_and_batch_fusion is not None:
        options.experimental_optimization.map_and_batch_fusion = \
        FLAGS.map_and_batch_fusion
    dataset = dataset.with_options(options)

    if self._use_fake_data:
      dataset = dataset.take(1).cache().repeat()

    return dataset