如何传递 Python 中的文件、BytesIO、StringIO 以备后用?

How to pass file, BytesIO, StringIO in Python to be used later?

这三个内存或磁盘缓冲区遵循相同的访问模式。我要重点 BytesIO.

如何传入文件或缓冲区对象供以后使用? 我在以下用例中遇到了很多麻烦:

def get_file_and_metadata():
  metadata = {"foo": "bar"}

  with io.BytesIO() as f: 
   f.write(b'content')
   f.seek(0)
   return f, metadata

f, metadata = get_file_and_metadata()

# Do something with file 
pd.read_csv(f, encoding="utf-8")

我怀疑是因为 f.close() 在 return 语句之后是 运行。

with 关键字将在文件脱离上下文后自动关闭文件,即脱离 with 代码块(因此称为上下文管理器)。

相反,创建 file_obj 和 return。

def get_file_and_metadata():
  metadata = {"foo": "bar"}

  f = io.BytesIO() 
  f.write(b'content')
  f.seek(0)
  return f, metadata

f, metadata = get_file_and_metadata()

# Do something with file 
print(f.read())
f.close()
del f # to remove it from memory.
with 套件终止时,

close 是 运行。如果你想传回一个打开的 file-like 对象,你不应该在 with 中打开它。一种选择是完全放弃上下文管理器并将其留给调用者来清理对象。

def get_file_and_metadata():
    metadata = {"foo": "bar"}
    f = o.BytesIO() 
    f.write(b'content')
    f.seek(0)
    return f, metadata

f, metadata = get_file_and_attr()
try:
    # Do something with file 
    pd.read_csv(f, encoding="utf-8")
finally:
    f.close()

每当文件对象通过某种管道传递或以给​​上下文管理器带来不便的顺序使用时,这样做都是合理的。至少在 cpython 中,99% 的时间文件在删除对象时关闭。

或者您可以编写自己的上下文管理器

import contextlib

@contextlib.contextmanager
def get_file_and_metadata():
    metadata = {"foo": "bar"}
    f = o.BytesIO() 
    f.write(b'content')
    f.seek(0)
    try:
        yield f, metadata
    finally:
        f.close()

with get_file_and_attr() as f, metadata:
    # Do something with file 
    pd.read_csv(f, encoding="utf-8")

根据您的评论,我意识到元数据可以直接放在 BytesIO 对象上,然后它的上下文管理器就可用了。

import io

def get_file_and_metadata():
    metadata = {"foo": "bar"}
    f = io.BytesIO()
    f.write(b'content')
    f.seek(0)
    f.metadata = metadata
    return f

with get_file_and_metadata() as f:
    pd.read_csv(f, encoding="utf-8")