2017-05-08 31 views
2

我有一個使用生成器編寫csv文件的Python(3.4)例程。但是,根據參數集,可能沒有任何數據,在這種情況下,我不希望寫入csv文件。 (它只會用一個頭文件寫入文件)。如果沒有數據,請勿使用csv DictWriter編寫文件

現在,bandaid是計算生成後的行數,然後刪除文件,但肯定必須有更好的方法,同時保留生成器是唯一的代碼,它知道是否有數據給定參數,(也有兩次在發電機調用):

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)) 
+0

您還可以看看http://stackoverflow.com/questions/661603/how-do-i-know-如果生成器是空的從一開始或http://stackoverflow.com/questions/3114252/one-liner-to-check-whether-an-iterator-yields-at-least-one-元件。 –

回答

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) 

或者這不使用可見try/catch代碼(雖然可能有等量下來裏面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) 

(這是由斯蒂芬勞赫的回答啓發,但有一些調整。)

+0

是的,但現在我擔心如何避免捕獲StopIteration錯誤,它會在first()之後以某種方式從代碼中冒出來。可能必須定義一個輔助變量... –

+0

我認爲這基本上是我所做的。一分鐘後,我想我需要在獲得first()後設置一個助手變量,以表明它是否成功。然後我記得'try' /'except' /'else',這是完美的。 –

+0

我原本是在我的代碼中使用else,但不知道OP在哪裏,所以變得更簡單一些。鏈子很聰明... –

1

您可以按使用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) 

請注意,這沒有經過測試,因此可能包含愚蠢的錯別字。

+1

'next'不會*預覽*發生器,它會*推進*它。你會失去這些數據! –

+0

@ juanpa.arrivillaga,感謝您的領導,但數據被保留,檢查代碼。我試圖用*斜體*預覽,但用它缺乏一個更好的詞... –

+1

也許「恐嚇報價」將是適當的? –