Как придать классу функциональность с оператором? - PullRequest
5 голосов
/ 22 ноября 2011

[Я прошу прощения за неумелый заголовок; Я не мог придумать ничего лучшего. Приветствуются предложения по улучшению названия.]

Я хочу реализовать интерфейс к HDF5 файлам, который поддерживает параллелизм на уровне нескольких процессов посредством блокировки файлов. Средой, предназначенной для этого модуля, является кластер Linux с доступом к общему диску через NFS. Цель состоит в том, чтобы включить параллельный доступ (через NFS) к одному и тому же файлу несколькими параллельными процессами, запущенными на нескольких разных хостах.

Я хотел бы иметь возможность реализовать функцию блокировки через класс-оболочку для класса h5py.File. (<a href="http://code.google.com/p/h5py/" rel="noreferrer">h5py</a> уже предлагает поддержку thread -уровня параллелизма, но базовая библиотека HDF5 не является поточно-ориентированной.)

Было бы здорово, если бы я мог что-то сделать в духе из этого:

class LockedH5File(object):
    def __init__(self, path, ...):
        ...
        with h5py.File(path, 'r+') as h5handle:
            fcntl.flock(fcntl.LOCK_EX)
            yield h5handle
        # (method returns)

Я понимаю, что приведенный выше код неверен, но я надеюсь, что он передает основную идею: а именно, чтобы выражение LockedH5File('/path/to/file') доставляло открытый дескриптор клиентского кода, который затем может выполнять различные произвольные операции чтения / записи над Это. Когда этот дескриптор выходит из области видимости, его деструктор закрывает дескриптор, освобождая тем самым замок.

Цель, которая мотивирует это соглашение, состоит из двух частей:

  1. отделить создание дескриптора (с помощью кода библиотеки) из операций, которые впоследствии запрашиваются на дескрипторе (с помощью кода клиента), и

  2. убедитесь, что ручка закрыта и замок снят, независимо от того, что происходит во время выполнение промежуточного кода (например, исключения, необработанные сигналы, внутренние ошибки Python).

Как мне добиться этого эффекта в Python?

Спасибо!

Ответы [ 3 ]

8 голосов
/ 22 ноября 2011

объекты, которые можно использовать в операторах with, называются менеджерами контекста; и они реализуют простой интерфейс. Они должны предоставлять два метода: метод __enter__, который не принимает аргументов и может возвращать что-либо (которое будет присвоено переменной в части as), и метод __exit__, который принимает три аргумента ( заполняется результатом sys.exc_info()) и возвращает ненулевое значение, чтобы указать, что исключение было обработано. Ваш пример, вероятно, будет выглядеть так:

class LockedH5File(object):
    def __init__(self, path, ...):
        self.path = path

    def __enter__(self):
        self.h5handle = h5handle = h5py.File(self.path, 'r+')
        fcntl.flock(fcntl.LOCK_EX)
        return h5handle

    def __exit__(self, exc_type, exc_info, exc_tb):
        self.h5handle.close()
6 голосов
/ 22 ноября 2011

Для этого вашему классу необходимо реализовать протокол диспетчера контекста .В качестве альтернативы, напишите генераторную функцию, используя декоратор contextlib.contextmanager* 1005. *.

Ваш класс может выглядеть примерно так (детали использования h5py, вероятно, ужасно неверны):

class LockedH5File(object):
    def __init__(self, path, ...):
        self.h5file = h5py.File(path, 'r+')
    def __enter__(self):
        fcntl.flock(fcntl.LOCK_EX)
        return self.h5file
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.h5file.close()
2 голосов
/ 22 ноября 2011

Ну, менеджер контекста и оператор with. В общем, деструкторы в Python не гарантированно запускают на всех , поэтому вы не должны полагаться на них как на что-либо, кроме отказоустойчивой очистки. Укажите методы __enter__ и __exit__ и используйте их как

with LockedFile(...) as fp:
    # ...
...