为什么TextIOWrapper关闭给定的BytesIO流? [英] Why is TextIOWrapper closing the given BytesIO stream?

查看:66
本文介绍了为什么TextIOWrapper关闭给定的BytesIO流?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

If I run following code in 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流保持打开状态.例如,如果我将函数修改为

While if I don't use the TextIOWrapper the io stream stays open. As an example if I modify my function to

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

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

I don't get any errors any more so for some reason TestIOWrapper is closing the stream from which I would like to read afterwards. Is this intended to be like this and whether it is is there a way to achieve what I am trying without writing the csv writer myself?

推荐答案

csv 模块在这里很奇怪.包裹其他对象的大多数类似文件的对象都拥有该对象的所有权,它们在关闭(或以其他方式清除)后会关闭.

The csv module is the weird one here; most file-like objects that wrap other objects assume ownership of the object in question, closing it when they themselves are closed (or cleaned up in some other way).

避免出现此问题的一种方法是在允许清除之前,将 Text 与 TextIOWrapper 明确地分离:

One way to avoid the problem is to explicitly detach from the TextIOWrapper before allowing it to be cleaned up:

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 ,并且当<代码>关闭-或以其他方式清除.

The only other built-in way to avoid this is for real file objects, where you can pass them a file descriptor and closefd=False and they won't close the underlying file descriptor when close-ed or otherwise cleaned up.

当然,在您的特定情况下,有一种更简单的方法:只需使您的函数期望基于文本的文件状对象,然后使用它们而无需重新包装;您的函数实际上不应该负责在调用者的输出文件上强加编码(调用者想要UTF-16输出该怎么办?).

Of course, in your particular case, there is simpler way: Just make your function expect text based file-like objects and use them without rewrapping; your function really shouldn't be responsible for imposing encoding on the caller's output file (what if the caller wanted UTF-16 output?).

然后您可以执行以下操作:

Then you can do:

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'))

这篇关于为什么TextIOWrapper关闭给定的BytesIO流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆