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

时间:2018-03-27 22:02:24

标签: python file contextmanager

在主要方法中,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完成后关闭这些文件对象而不破坏封装?

我看到的潜在解决方案:

  • 不传递passed_object:传递passed_object.create_file()passed_object.attr1,然后在主with open...中使用上下文管理器。但是,我现在必须将每个属性/创建的对象传递给my_class

  • 编写方法my_object.close_file(),并从main调用它。这似乎打破了封装,因为主要不应该知道这一点。

  • 编写一个关闭文件的my_object.__del__()方法。

  • 不要担心关闭它;你的程序会以几行结束。

1 个答案:

答案 0 :(得分:1)

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

  • PassedObject.create_file只是打开一个文件,返回它并且不保留对它的引用
  • 文件的使用仅限于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.')