python - 为什么 TextIOWrapper 关闭给定的 BytesIO 流?

标签 python python-3.x csv bytesio

如果我在 python 3 中运行以下代码

from io import BytesIO
import csv
from io import TextIOWrapper


def fill_into_stringio(input_io):
    writer = csv.DictWriter(TextIOWrapper(input_io, encoding='utf-8'),fieldnames=['ids'])
    for i in range(100):
        writer.writerow({'ids': str(i)})

with BytesIO() as input_i:
    fill_into_stringio(input_i)
    input_i.seek(0)

我得到一个错误:

ValueError: I/O operation on closed file.

如果我不使用 TextIOWrapper,io 流将保持打开状态。例如,如果我将函数修改为

def fill_into_stringio(input_io):
    for i in range(100):
        input_io.write(b'erwfewfwef')

我没有再收到任何错误,因此出于某种原因,TestIOWrapper 正在关闭我之后想从中读取的流。这是打算像这样吗?是否有一种方法可以在不自己编写 csv 编写器的情况下实现我正在尝试的目标?

最佳答案

csv 模块在这里很奇怪;大多数包装其他对象的类文件对象都拥有相关对象的所有权,并在它们自身关闭(或以其他方式清理)时将其关闭。

避免此问题的一种方法是在允许清理之前从 TextIOWrapper 显式分离:

def fill_into_stringio(input_io):
    # write_through=True prevents TextIOWrapper from buffering internally;
    # you could replace it with explicit flushes, but you want something 
    # to ensure nothing is left in the TextIOWrapper when you detach
    text_input = TextIOWrapper(input_io, encoding='utf-8', write_through=True)
    try:
        writer = csv.DictWriter(text_input, fieldnames=['ids'])
        for i in range(100):
            writer.writerow({'ids': str(i)})
    finally:
        text_input.detach()  # Detaches input_io so it won't be closed when text_input cleaned up

避免这种情况的唯一其他内置方法是针对真实文件对象,您可以向它们传递一个文件描述符和 closefd=False 并且当 close-ed 或以其他方式清理。

当然,在您的特定情况下,有更简单的方法:只需让您的函数期望基于文本的类文件对象并在不重新包装的情况下使用它们;你的函数真的不应该负责对调用者的输出文件进行编码(如果调用者想要 UTF-16 输出怎么办?)。

然后你可以这样做:

from io import StringIO

def fill_into_stringio(input_io):
    writer = csv.DictWriter(input_io, fieldnames=['ids'])
    for i in range(100):
        writer.writerow({'ids': str(i)})

# newline='' is the Python 3 way to prevent line-ending translation
# while continuing to operate as text, and it's recommended for any file
# used with the csv module
with StringIO(newline='') as input_i:
    fill_into_stringio(input_i)
    input_i.seek(0)
    # If you really need UTF-8 bytes as output, you can make a BytesIO at this point with:
    # BytesIO(input_i.getvalue().encode('utf-8'))

关于python - 为什么 TextIOWrapper 关闭给定的 BytesIO 流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48434423/

相关文章:

python - 如何过滤字典值(在另一个字典中)

python - 属性错误: 'DataFrame' object has no attribute 'Class'

python - Flask.Response 在网络浏览器中无法开始下载

python - 从列中删除重复的单词

python - Pandas 用不同大小的 block 替换行 block

python - Python 中 "round"函数的行为

javascript - 2个像python一样的IF条件相等

python - 迁移到 1.2.5 后 Django 测试失败 - 子模型的主键问题

Python将多个不相等的列表写入Excel文件

Python Pandas 使用不同日期读取多个 Excel 文件