2017-05-08 87 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

也许“恐吓报价”将是适当的? –