Не записывать файл с помощью csv DictWriter, если нет данных

У меня есть подпрограмма Python (3.4), которая записывает файл csv с помощью генератора. Однако, в зависимости от набора параметров, данных может не быть, и в этом случае я не хочу, чтобы файл csv записывался. (Он просто запишет файл только с заголовком).

Прямо сейчас бандад состоит в том, чтобы подсчитать строки после генерации, а затем удалить файл, но, безусловно, должен быть лучший способ, сохраняя при этом шаблон, в котором генератор является единственным кодом, который знает, есть ли данные для заданных параметров. , (без необходимости дважды вызывать генератор):

def write_csv(csv_filename, fieldnames, generator, from_date, to_date, client=None):
  with open(csv_filename, 'w', newline='') as csv_file:
    csv_writer = csv.DictWriter(csv_file, fieldnames=fieldnames, delimiter='\t')
    csv_writer.writeheader()
    csv_writer.writerows(generator(from_date, to_date, client))

  # If no rows were written delete the file, we don't want it
  with open(csv_filename) as f:
    lines = sum(1 for _ in f)
    if lines == 1:
      f.close()
      os.remove(f.name)


def per_client_items_generator(from_date, to_date, client):
  return (per_client_detail(client, sales_item) for sales_item in
        sales_by_client.get(client))

person uncrase    schedule 08.05.2017    source источник
comment
Вы также можете взглянуть на stackoverflow.com/questions/661603/ или stackoverflow.com/questions/3114252/.   -  person Matthias Fripp    schedule 09.05.2017


Ответы (2)


Вы можете использовать itertools, чтобы взглянуть на первый элемент, а затем как бы вернуть его в генератор:

import itertools
gen = generator(from_date, to_date, client)
try:
    # try to get an element
    first = next(gen)
except StopIteration:
    pass
else:
    # run this if there was no exception:
    gen = itertools.chain([first], gen)
    csv_writer.writeheader()
    csv_writer.writerows(gen)

Это немного короче, но может быть труднее читать:

import itertools
gen = generator(from_date, to_date, client)
try:
    # pop an element then chain it back in
    gen = itertools.chain([next(gen)], gen)
except StopIteration:
    pass
else:
    # run this if there was no exception:
    csv_writer.writeheader()
    csv_writer.writerows(gen)

Или здесь не используется видимый _3 _ / _ 4_ код (хотя внутри next(), вероятно, столько же):

import itertools
sentinel = object()  # special flag that couldn't come from the generator
gen = generator(from_date, to_date, client)

# try to get something
first = next(gen, sentinel)
if first is not sentinel:
    # got a meaningful item, put it back in the generator
    gen = itertools.chain([first], gen)
    csv_writer.writeheader()
    csv_writer.writerows(gen)

(Они были вдохновлены ответом Стивена Рауха, но с некоторыми изменениями.)

person Matthias Fripp    schedule 08.05.2017
comment
Да, но теперь я беспокоюсь о том, как избежать ошибок StopIteration, которые каким-то образом всплывают из кода после first (). Возможно, придется определить вспомогательную переменную ... - person Matthias Fripp; 09.05.2017
comment
Думаю, в основном то, что я сделал. На минуту я подумал, что мне нужно установить вспомогательную переменную после получения first (), чтобы указать, удалось ли это. Затем я вспомнил _1 _ / _ 2 _ / _ 3_, который идеально подходит для этого. - person Matthias Fripp; 09.05.2017
comment
Изначально я использовал else в своем коде, но не был уверен, где находится OP, поэтому пошел немного проще. Цепочка умная ... - person Stephen Rauch; 09.05.2017
comment
Спасибо! Только что заметил, что пара ответов на stackoverflow.com/questions/661603/ похожи на мои, но так оно и есть! - person Matthias Fripp; 09.05.2017

Вы можете отсортировать предварительный просмотр генератора, используя next(), стараясь сохранить первое сгенерированное значение, например:

csv_gen = generator(from_date, to_date, client)
try:
    first_item = next(csv_gen)
except StopIteration:
    csv_gen = None

if csv_gen is not None:
     # prep for write csv
     ....         

    # write csv header
    csv_writer.writeheader()

    # write item already read from generator
    csv_writer.writerow(first_item)        

    # write rest of generator
    csv_writer.writerows(csv_gen)

Обратите внимание, что это не было проверено, поэтому может содержать глупые опечатки.

person Stephen Rauch    schedule 08.05.2017
comment
next не предварительный просмотр генератора, он продвигает его. Вы потеряете эти данные! - person juanpa.arrivillaga; 09.05.2017
comment
@ juanpa.arrivillaga, Спасибо за внимание, но данные сохранились, проверьте код. Я попытался выделить preview курсивом, но использовал его из-за отсутствия лучшего слова ... - person Stephen Rauch; 09.05.2017
comment
Возможно, будут уместны напуганные цитаты? - person juanpa.arrivillaga; 09.05.2017