Обертка класса вокруг файла - правильный способ закрыть дескриптор файла, когда на него больше нет ссылок - PullRequest
10 голосов
/ 21 декабря 2011

У меня есть класс, который включает в себя некоторые необходимые мне функции обработки файлов.Другой класс создает экземпляр filehandler и использует его в течение неопределенного времени.В конце концов, caller уничтожается, что уничтожает единственную ссылку на filehandler.

. Каков наилучший способ filehandler закрыть файл?

, который я сейчас использую__del__(self) но увидев несколько разных вопросов и статей , у меня сложилось впечатление, что это плоховещь .

class fileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefile = open(dbf, 'rb')
    def __del__(self):
        self.thefile.close()

Это важный бит обработчика.Весь смысл этого класса в том, чтобы абстрагироваться от деталей работы с базовым файловым объектом, а также избежать ненужного чтения всего файла в память.Однако часть обработки базового файла закрывает его, когда объект выпадает из области видимости.

Предполагается, что caller не должен знать или заботиться о деталях, связанных с filehandler.Задача filehandler - высвобождать все необходимые ресурсы, когда они выпадают из области видимости.Это одна из причин, по которой он был абстрагирован в первую очередь.Итак, я, похоже, столкнулся с перемещением кода filehandler в вызывающий объект или с утечкой абстракции.

Мысли?

Ответы [ 2 ]

13 голосов
/ 21 декабря 2011

__del__ само по себе не плохо. Вам просто нужно быть очень осторожным, чтобы не создавать ссылочные циклы в объектах, для которых определено __del__. Если вам действительно нужно создать циклы (родитель относится к потомку, который ссылается на родителя), тогда вы захотите использовать модуль weakref.

Итак, __del__ нормально, просто будьте осторожны с циклическими ссылками.

Сборка мусора. Важным моментом здесь является то, что когда объект выходит из области видимости, он может быть сборщиком мусора, и фактически будет сборщиком мусора ... но когда? Там нет никакой гарантии, когда, и разные реализации Python имеют разные характеристики в этой области. Поэтому для управления ресурсами лучше быть явным и либо добавить .close() в свой filehandler, либо, если ваше использование совместимо, добавить методы __enter__ и __exit__.

Здесь описаны методы __enter__ и __exit__ . Одна очень хорошая вещь о них заключается в том, что __exit__ вызывается даже при возникновении исключений, так что вы можете считать или ваши ресурсы закрыты изящно.

Ваш код, улучшенный для __enter__ / __exit__:

class fileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefilename = dbf
    def __enter__(self):
        self.thefile = open(self.thefilename, 'rb')
        return self
    def __exit__(self, *args):
        self.thefile.close()

Обратите внимание, что файл открывается в __enter__ вместо __init__ - это позволяет вам создать объект обработчика файлов один раз, а затем использовать его всякий раз, когда вам нужно, в with без его воссоздания:

fh = filehandler('some_dbf')
with fh:
    #file is now opened
    #do some stuff
#file is now closed
#blah blah
#need the file again, so
with fh:
    # file is open again, do some stuff with it
#etc, etc 
6 голосов
/ 21 декабря 2011

Как вы уже написали, класс не делает файл более надежным. Если вы просто отбросите экземпляр обработчика файлов на полу, файл не закроется, пока объект не будет уничтожен. Это может произойти немедленно или может не произойти до тех пор, пока объект не будет собран мусором, но простое удаление простого файлового объекта на пол закроет его так же быстро. Если единственная ссылка на thefile находится внутри вашего объекта класса, то при filehandler сборщик мусора thefile также будет собирать мусор и, следовательно, закрываться одновременно.

Правильный способ использования файлов - использовать оператор with:

with open(dbf, 'rb') as thefile:
    do_something_with(thefile)

, который гарантирует, что thefile всегда будет закрыт при выходе из условия with. Если вы хотите обернуть файл в другой объект, вы можете сделать это тоже, определив методы __enter__ и __exit__:

class FileHandler:
    def __init__(self, dbf):
        self.logger = logging.getLogger('fileHandler')
        self.thefile = open(dbf, 'rb')
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        self.thefile.close()

и тогда вы можете сделать:

with FileHandler(dbf) as fh:
    do_something_with(fh)

и убедитесь, что файл быстро закрывается.

...