python - 从关闭的文件中返回生成器 | csv 阅读器不返回生成器作为 openpyxl?

标签 python python-3.x iterator generator openpyxl

我正在读取一个 xlsx 文件(使用 openpyxl)和一个 csv 文件(使用 csv.reader)。 openpyxl 正确返回生成器,在从区分文件是 excel 文件还是 csv 的函数返回后,我可以迭代生成器中的值。当我对 csv 文件执行相同的操作时,就会出现问题,您会看到它返回一个生成器,但我无法迭代它,因为 csv 文件在 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

最佳答案

您不能将 with 语句与 openpxyl 函数一起使用。但似乎您已经知道问题所在,即您正在尝试在 with block 关闭文件处理程序后对其进行迭代。早点迭代?或者更好的是,从 reader 对象中yield:

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

另请注意两件事:

  1. 添加 yield from/yield 使 get_rows 函数成为生成器函数,从而更改 semantics of the return iter_rows(worksheet1) line 。您现在想要从两个分支中yield

  2. 当您有“csv”时,您最初编写get_rows的方式不会返回生成器。 csv.reader 对象不是生成器,(也不是,我相信 worksheet.iter_rows 对象,但我不知道,因为我不使用 openpyxl)。 “xlsx”分支返回生成器的原因是您显式返回对已定义为生成器的 iter_rows 的调用。您的“csv”分支返回一个 csv.reader 对象。后者是一个惰性迭代器,但它不是一个生成器。前者一个生成器。并非所有可迭代对象都是生成器,但生成器是作为一种语言构造添加的,以方便可迭代对象的编写,但现在已扩展为能够执行各种奇特的操作,例如协程。请参阅this answer一个著名的问题,我认为它比公认的答案更好。

关于python - 从关闭的文件中返回生成器 | csv 阅读器不返回生成器作为 openpyxl?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44750863/

相关文章:

python - 为什么我的脚本告诉我无法访问端口 25565 上的 Minecraft 服务器?

python - 哪个 Python IDE 具有 Visual Studio 功能?

python - 从 stat().st_mtime 到 datetime?

python - pip install libvirt-python 在 vi​​rtualenv 中失败

django - 在 Django Admin 中只获取与实例相关的项目

java - 获取 Java 的列表迭代器以返回 Object 以外的内容

Python 名称错误 : Name is not defined Linux

python - 如何使用 python 从 MySQL 下载 BLOB .docx 文件?

c++ - 错误 C2440 : 'initializing' : cannot convert from 'std::_Vector_iterator<_Ty,_Alloc>' to 'type *'

c++ - std::find() 返回的迭代器不可取消引用