【问题标题】:python class file context managerpython类文件上下文管理器
【发布时间】:2023-07-16 08:38:01
【问题描述】:

我正在尝试以这种方式打开一个类中的文件并在退出时将其关闭。

class PlanetaryImage(object):
    @classmethod
    def open(cls, filename):
        with open(filename, 'rb') as fp:
            return cls(fp, filename)

    def __init__(self, stream, filename=None, memory_layout='DISK'):
        self.filename = filename
        self._parse_data(stream)

    def _parse_data(self, stream):
        data_stream = stream
        try:
            if self.data_filename is not None:
                dirpath = os.path.dirname(self.filename)
                data_file = os.path.abspath(
                    os.path.join(dirpath, self.data_filename))

                data_stream = open(data_file, 'rb')

            data_stream.seek(self.start_byte)
            if self.format in self.BAND_STORAGE_TYPE:
                return getattr(self, self.BAND_STORAGE_TYPE[self.format])(data_stream)

            raise Exception('Unkown format (%s)' % self.format)

        finally:
            data_stream.close()

在某些情况下,我必须在_parse_data 函数中再打开一个文件。我想使用with,但if 语句使它变得困难。有关如何使 try 部分更 Pythonic 的任何建议。

【问题讨论】:

  • 您不能跨代码块使用withfpPlanetaryImage.open 返回后立即关闭。如果您希望fp 在较长时间内保持打开状态,则需要在适当的时候手动将其关闭。
  • 我不知道确切的原因,但fp 似乎没有关闭。我可以通过其他方法访问流
  • 你真的在任何地方使用PlanetaryImage.open吗?
  • 我刚刚编辑了代码。我在 init 中调用所有需要流的函数本身可能是with 的上下文,然后恰好是__init__
  • 我是这样用的PlanetaryImage.open(filename)

标签: python with-statement


【解决方案1】:

_parse_data 没有理由尝试打开文件。调用者应负责将PlanetaryImage.open 与文件名一起使用或提供__init__ 的打开文件句柄。 _parse_data 应该只做一件事:从它的流参数中解析数据。

class PlanetaryImage(object):
    @classmethod
    def open(cls, filename):
        with open(filename, 'rb') as fp:
            return cls(fp, filename)

    def __init__(self, stream, memory_layout='DISK'):
        self._parse_data(stream)

    def _parse_data(self, data_stream):
        try:        
            data_stream.seek(self.start_byte)
            if self.format in self.BAND_STORAGE_TYPE:
                return getattr(self, self.BAND_STORAGE_TYPE[self.format])(data_stream)

            raise Exception('Unkown format (%s)' % self.format)

        finally:
            data_stream.close()

现在,使用该类只有两个选项:

with open(filename, 'rb') as fp:
    x = PlanetaryImage(fp)
    ...

x = PlanetaryImage(filename)
....

【讨论】:

  • 你说得对。问题是parse_data 函数中的if 部分。 self.data_filename 是一个属性,表示我是否需要打开一个新文件(在某些情况下,openend 文件通常指向另一个文件)。这就是 try finally 块背后的原因
  • 能够在第三个位置(除了外部和PlanetaryImage.open)打开文件是不必要的复杂化。通过完全删除 if 语句并确保首先将正确的类似文件的对象传递给 _parse_data ,您的问题就消失了,并且您没有提供一个令人信服的理由为什么需要保留它;鉴于您发布的代码,当然没有理由。
  • 也许这会给你一个更好的主意github.com/planetarypy/planetaryimage/pull/12
  • 它没有。确保打开正确文件所需的任何操作都可以在 __init__ 中完成,以便 _parse_data 可以假定它已被赋予正确的类文件对象。