Нужно ли реализовывать шаблон Dispose для оболочки ресурсов в Python - PullRequest
0 голосов
/ 02 ноября 2018

Если я собираюсь реализовать безопасную оболочку ресурсов в Python, нужно ли мне применять шаблон Dispose , такой как C #?

Вот демонстрационная реализация того, что я имею в виду:

class ResourceWrapper:
    def __init__(self):
        self._python_resource = ...  # A Python object that manages some resources.
        self._external_resource = _allocate_resource()  # A resource handle to an external resource.
        self._is_closed = False  # Whether the object has been closed.

    def __del__(self):
        self._close(manual_close=False)  # Called by GC.

    def close(self):
        self._close(manual_close=True)  # Called by user to free resource early.

    def _close(self, manual_close):
        if not self._is_closed:  # Don’t want a resource to be closed more than once.
            if manual_close:
                # Since `_close` is called by user, we can guarantee that `self._python_resource` is still valid, so we
                # can close it safely.
                self._python_resource.close() 
            else:
                # This means `_close` is called by GC, `self._python_resource` might be already GCed, but we don’t know
                # for sure, so we do nothing and rely on GC to free `self._python_resource`.

                pass

            # GC will not take care of freeing unmanaged resource, so whether manual close or not, we have to close the
            # resource to prevent leaking.

            _free_resource(self._external_resource)

            # Now we mark the object as closed to prevent closing multiple times.

            self._is_closed = True

self._python_resource - это объект-оболочка ресурса, управляемый Python GC, а self._external_resource - дескриптор внешнего ресурса, который не управляется Python GC.

Я хочу убедиться, что как управляемые, так и неуправляемые ресурсы освобождаются, если руководство пользователя закрывает оболочку, и они также освобождаются, если объект оболочки получает GCed.

1 Ответ

0 голосов
/ 02 ноября 2018

Нет, в Python вы должны использовать Context Managers :

class ResourceWrapper:
    def __init__(self):
        ...

    ...


    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._close(manual_close=False)

with ResourceWrapper() as wrapper:
    # do something with wrapper

Примечание 1: Этот комментарий есть в _close() метод:

Это означает, что _close вызывается GC, self._python_resource может быть уже GCed, но мы не знаем наверняка, поэтому мы ничего не делаем и полагаемся на GC бесплатно self._python_resource.

Я не уверен, что вы подразумеваете под этим, но пока вы держите ссылку на объект (и если это не слабая ссылка ), это не будет GC ' ред.

Примечание 2: Что произойдет, если объект, являющийся диспетчером контекста, используется без блока with? Тогда ресурс будет освобожден, когда объект будет собран мусором - но я бы не стал беспокоиться об этом. Использование менеджеров контекста - распространенная идиома в python (см. Любой пример с файлом open() ing). Если это важно для вашего приложения, вы можете получить ресурсы в __enter__(), таким образом, вы не получите, если не в блоке with.

Примечание 3, о циклических ссылках: Если у вас есть два объекта, которые содержат ссылку друг на друга, вы сформировали циклическую ссылку, так что два объекта не будут освобождены «обычной» ссылкой. считая GC. Вместо этого они должны собираться поколением ГК, , если только не будет иметь метод __del__. __del__ запрещает GC собирать предметы. См gc.garbage:

Список объектов, которые коллекционер счел недоступными, но не может быть освобожден (не коллекционируемые объекты). По умолчанию этот список содержит только объекты с __del__() methods. [1] Объекты, которые имеют __del__() методы и являются частью эталонного цикла, что делает весь эталонный цикл не подлежащим сбору, включая объекты не обязательно в цикле, но достижимо только из него.

Представлен Python 3.4 PEP-442 , который вводит безопасное завершение объекта. В любом случае, у вас не будет недействительных ссылок. Если у вас есть атрибут (hasattr(self, "_python_resource")), он будет действителен.

Еда на вынос: не используйте __del__.

...