从一个关闭的文件中返回生成器 | csv reader 没有将生成器作为 openpyxl 返回?
Returing generator from a closed file | csv reader not returning a generator as openpyxl?
我正在读取一个 xlsx 文件(使用 openpyxl)和一个 csv(使用 csv.reader)。 openpyxl return 是一个正确的生成器,我可以在生成器中的值从一个区分文件是 excel 文件还是 csv 的函数 returned 后迭代。当我对 csv 文件做同样的事情时,问题就出现了,你看到它 return 是一个生成器,但我无法迭代它,因为 csv 文件似乎在函数 return 之后关闭with 语句。我知道很明显文件在 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
问题与您处理文件的方式有关。函数中的两个分支 return 都是迭代器,但 CSV 分支使用 with
语句,当您 return
时自动关闭文件。这意味着您获得的迭代器 csv.reader
是无用的,因为当您的顶级代码可以使用它时,它尝试读取的文件已经关闭。
解决此问题的一种方法是使您的 get_rows
函数成为生成器。如果您 yield from
csv.reader
而不是 return
ing 它,文件将不会关闭,直到它被完全读取(或生成器被丢弃)。您还需要 yield from
您为其他分支编写的 iter_rows
生成器。
您没有将 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
的方式不会 return 生成器。 csv.reader
对象不是生成器(我相信 worksheet.iter_rows
对象也不是,但我不知道,因为我不使用 openpyxl
)。您的 "xlsx" 分支 return 是生成器的原因是因为您显式 return 调用了 iter_rows
,您已将其定义为生成器。您的 "csv" 分支 return 是一个 csv.reader
对象。后者是惰性可迭代对象,但它不是生成器。前者是一个生成器。并非所有的可迭代对象都是生成器,但是生成器是作为一种语言结构添加的,以促进可迭代对象的编写,但现在已经扩展到能够做各种奇特的事情,比如协程。参见this answer一个著名的问题,我认为它比接受的答案更好。
我正在读取一个 xlsx 文件(使用 openpyxl)和一个 csv(使用 csv.reader)。 openpyxl return 是一个正确的生成器,我可以在生成器中的值从一个区分文件是 excel 文件还是 csv 的函数 returned 后迭代。当我对 csv 文件做同样的事情时,问题就出现了,你看到它 return 是一个生成器,但我无法迭代它,因为 csv 文件似乎在函数 return 之后关闭with 语句。我知道很明显文件在 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
问题与您处理文件的方式有关。函数中的两个分支 return 都是迭代器,但 CSV 分支使用 with
语句,当您 return
时自动关闭文件。这意味着您获得的迭代器 csv.reader
是无用的,因为当您的顶级代码可以使用它时,它尝试读取的文件已经关闭。
解决此问题的一种方法是使您的 get_rows
函数成为生成器。如果您 yield from
csv.reader
而不是 return
ing 它,文件将不会关闭,直到它被完全读取(或生成器被丢弃)。您还需要 yield from
您为其他分支编写的 iter_rows
生成器。
您没有将 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 thereturn iter_rows(worksheet1)
line。您现在想要yield from
两个分支。当你有 "csv" 时,你最初写
get_rows
的方式不会 return 生成器。csv.reader
对象不是生成器(我相信worksheet.iter_rows
对象也不是,但我不知道,因为我不使用openpyxl
)。您的 "xlsx" 分支 return 是生成器的原因是因为您显式 return 调用了iter_rows
,您已将其定义为生成器。您的 "csv" 分支 return 是一个csv.reader
对象。后者是惰性可迭代对象,但它不是生成器。前者是一个生成器。并非所有的可迭代对象都是生成器,但是生成器是作为一种语言结构添加的,以促进可迭代对象的编写,但现在已经扩展到能够做各种奇特的事情,比如协程。参见this answer一个著名的问题,我认为它比接受的答案更好。