我正在阅读xlsx文件(使用openpyxl)和csv(使用csv.reader)。 openpyxl正确返回一个生成器,我可以在从一个区分文件是excel文件还是csv的函数返回后迭代生成器中的值。当我使用csv文件执行相同的操作时会出现问题,您会看到它返回一个生成器,但我无法迭代它,因为在with语句中函数返回后csv文件似乎已关闭。我知道很明显,在with语句填满它的目的之后文件关闭了,但为什么openpyxl工作呢?为什么我仍然可以迭代excel文件的生成器?而且,我的最终问题是,如何使csv.reader的行为与openpyxl在此处的行为相同,即我能够迭代生成器值。
import csv
from openpyxl import load_workbook
def iter_rows(worksheet):
"""
Iterate over Excel rows and return an iterator of the row value lists
"""
for row in worksheet.iter_rows():
yield [cell.value for cell in row]
def get_rows(filename, file_extension):
"""
Based on file extension, read the appropriate file format
"""
# read csv
if file_extension == 'csv':
with open(filename) as f:
return csv.reader(f, delimiter=",")
# read Excel files with openpyxl
if file_extension in ['xls', 'xlsx']:
wb2 = load_workbook(filename)
worksheet1 = wb2[wb2.get_sheet_names()[0]]
return iter_rows(worksheet1)
# this works properly
rows = get_rows('excels/ar.xlsx', 'xlsx')
print(rows) # I am: <generator object iter_rows at 0x074D7A58>
print([row for row in rows]) # I am printing each row of the excel file from the generator
# Error: ValueError: I/O operation on closed file
rows = get_rows('excels/ar.csv', 'csv')
print(rows) # I am: <generator object iter_rows at 0x074D7A58>
print([row for row in rows]) # ValueError: I/O operation on closed file
答案 0 :(得分:3)
您不会将with
语句与openpxyl
函数一起使用。但似乎你已经知道了这个问题,即在with
块关闭它之后你试图迭代文件处理程序。早点迭代?或者更好的是yield from
reader
对象:
def get_rows(filename, file_extension):
"""
Based on file extension, read the appropriate file format
"""
# read csv
if file_extension == 'csv':
with open(filename) as f:
yield from csv.reader(f, delimiter=",")
# read Excel files with openpyxl
if file_extension in ['xls', 'xlsx']:
wb2 = load_workbook(filename)
worksheet1 = wb2[wb2.get_sheet_names()[0]]
yield from iter_rows(worksheet1)
或者,如果您使用的是Python 2:
def get_rows(filename, file_extension):
"""
Based on file extension, read the appropriate file format
"""
# read csv
if file_extension == 'csv':
with open(filename) as f:
for row in csv.reader(f, delimiter=",")
yield row
# read Excel files with openpyxl
if file_extension in ['xls', 'xlsx']:
wb2 = load_workbook(filename)
worksheet1 = wb2[wb2.get_sheet_names()[0]]
for row in iter_rows(worksheet1):
yield row
还要注意两件事:
添加yield from
/ yield
会使get_rows
函数成为生成函数,从而更改semantics of the return iter_rows(worksheet1)
line。您现在想要yield from
两个分支。
当您拥有“csv”时,您最初编写get_rows
的方式不会返回生成器。 csv.reader
对象不是生成器(也不是,我相信worksheet.iter_rows
个对象,但我不知道,因为我不使用openpyxl
)。 “xlsx”分支返回生成器的原因是因为您明确地将调用返回到iter_rows
,您已将其定义为生成器。您的“csv”分支返回csv.reader
个对象。后者是一个懒惰的可迭代,但它不是一个生成器。前是一个生成器。并非所有的迭代都是生成器,但是生成器被添加为语言构造以便于编写可迭代,但现在已经扩展为能够执行各种奇特的东西,例如协同程序。请参阅this answer一个着名的问题,我认为这个问题比接受的答案要好。
答案 1 :(得分:2)
问题与您处理文件的方式有关。函数中的两个分支都返回一个迭代器,但CSV分支使用with
语句在return
时自动关闭文件。这意味着你获得的迭代器csv.reader
是无用的,因为它尝试读取的文件在顶级代码可以使用它时已经关闭。
解决此问题的一种方法是使get_rows
函数成为生成器。如果您yield from
csv.reader
而不是return
,则在完全读取文件(或丢弃生成器)之前,文件不会关闭。您还需要yield from
为其他分支编写的iter_rows
生成器。