使用不同对象管理文件打开和关闭职责

Managing file open and close responsibilities with different objects

在 main 方法中,my_object 需要访问 passed_object 的多个成员,包括打开的文件(passed_file = passed_object.create_file()。例如:

import os

def main():

    print('Start of program...')
    passed_object = PassedObject()
    my_object = MyObject(passed_object)

    my_object.use_passed_object()
    print('End of program.')

class MyObject(object):

    def __init__(self, passed_object):
        self.passed_object = passed_object

    def use_passed_object(self):
        f = self.passed_object.create_file()
        print('attribute:')
        print(self.passed_object.attr1)
        print('contents of first file:')
        print(list(f))

class PassedObject(object):

    def __init__(self):

        self.attr1 = 'some attribute string'

    def create_file(self):

        path = '/tmp'
        files = [file for file in os.listdir(path) 
         if os.path.isfile(os.path.join(path, file))]

        f = open(files[0], 'r')
        return f

main()

问题:passed_object 创建了 my_object 所需的文件对象,以及此简单示例中未显示的其他文件对象。如何在 my_object 完成后关闭这些文件对象而不破坏封装?

我看到的潜在解决方案:

假设最简单的情况(因为缺少细节):

  • PassedObject.create_file 只是打开一个文件,returns 它并不保留对它的引用
  • 文件的使用仅限于MyObject.use_passed_object
  • 的范围

解决方法很简单:use_passed_object 完成后关闭文件:

class MyObject(object):

    def __init__(self, passed_object):
        self.passed_object = passed_object

    def use_passed_object(self):
        f = self.passed_object.create_file()
        try:
            print('attribute:')
            print(self.passed_object.attr1)
            print('contents of first file:')
            print(list(f))
        finally:
            f.close()

或者,由于 passed_object.create_file() 只是返回一个支持上下文管理器接口的 file 对象,您也可以这样做:

    def use_passed_object(self):
        with self.passed_object.create_file() as f:
            print('attribute:')
            print(self.passed_object.attr1)
            print('contents of first file:')
            print(list(f))

在更复杂的情况下(例如返回内置 file 以外的内容),您可以创建自己的上下文管理器,它封装了对 passed_object.create_file()...

的访问

另一方面,如果文件在其生命周期内被 MyObject 的多个方法使用,您需要一个围绕 MyObject 实例使用的上下文管理器。

为此,您必须:

  • 记住 MyObject 它打开了哪些文件(无论如何你都必须这样做才能在多种方法中使用它)
  • 执行 MyObject.close 关闭所有这些文件
class MyObject(object):

    def close(self):
        for file_object in self.opened_files:
            file_object.close()

然后实现上下文管理器并为此使用它。

选项 1:使用 contextlib.closing

import contextlib

def main():

    print('Start of program...')
    passed_object = PassedObject()

    with contextlib.closing(MyObject(passed_object)) as my_object:
        my_object.use_passed_object()

    print('End of program.')

选项 2:在 MyObject 本身

上实现上下文管理器接口
class MyObject(object):

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.close()


def main():

    print('Start of program...')
    passed_object = PassedObject()

    with MyObject(passed_object) as my_object:
        my_object.use_passed_object()

    print('End of program.')