Как правильно очистить объект Python?



class Package:
def __init__(self):
self.files = []

# ...

def __del__(self):
for file in self.files:
os.unlink(file)


__del__(self) выше не исключение AttributeError. Я понимаю Python не гарантирует существование "глобальных переменных" (данные членов в этом контексте?), когда __del__() вызывается. Если это так, и это является причиной исключения, как я могу убедиться, что объект разрушается правильно?

392   6  

6 ответов:

я бы рекомендовал использовать Python with заявление для управления ресурсами, которые должны быть очищены. Проблема с использованием явного close() утверждение заключается в том, что вы должны беспокоиться о том, что люди забывают называть его вообще или забывают поместить его в finally блок для предотвращения утечки ресурсов при возникновении исключения.

использовать with оператор, создайте класс со следующими методами:

  def __enter__(self)
  def __exit__(self, exc_type, exc_value, traceback)

в вашем примере выше, вы могли бы использовать

class Package:
    def __init__(self):
        self.files = []

    def __enter__(self):
        return self

    # ...

    def __exit__(self, exc_type, exc_value, traceback):
        for file in self.files:
            os.unlink(file)

затем, когда кто-то хотел использовать ваш класс, они делали следующее:

with Package() as package_obj:
    # use package_obj

переменная package_obj будет экземпляром типа Package (это значение, возвращаемое __enter__ метод). Его __exit__ метод будет вызван автоматически, независимо от того, происходит ли исключение.

вы могли бы даже сделать этот подход на шаг дальше. В приведенном выше примере кто-то все еще может создать экземпляр пакета с помощью его конструктора без использования with предложения. Ты же не хочешь, чтобы это случилось. Вы можете исправить это, создав класс PackageResource, который определяет __enter__ и __exit__ методы. Затем класс пакета будет определен строго внутри __enter__ метод и вернулись. Таким образом, вызывающий объект никогда не сможет создать экземпляр класса пакета без использования with о себе:

class PackageResource:
    def __enter__(self):
        class Package:
            ...
        self.package_obj = Package()
        return self.package_obj

    def __exit__(self, exc_type, exc_value, traceback):
        self.package_obj.cleanup()

вы бы использовали это следующим образом:

with PackageResource() as package_obj:
    # use package_obj

как приложение к ответ Клинта, вы можете упростить PackageResource используя contextlib.contextmanager:

@contextlib.contextmanager
def packageResource():
    class Package:
        ...
    package = Package()
    yield package
    package.cleanup()

альтернативно, хотя, вероятно, не как Pythonic, вы можете переопределить Package.__new__:

class Package(object):
    def __new__(cls, *args, **kwargs):
        @contextlib.contextmanager
        def packageResource():
            # adapt arguments if superclass takes some!
            package = super(Package, cls).__new__(cls)
            package.__init__(*args, **kwargs)
            yield package
            package.cleanup()

    def __init__(self, *args, **kwargs):
        ...

и with Package(...) as package.

чтобы сделать вещи короче, назовите свою функцию очистки close и использовать contextlib.closing в этом случае вы можете использовать немодифицированные Package класса по with contextlib.closing(Package(...)) или переопределить его __new__ к простому

class Package(object):
    def __new__(cls, *args, **kwargs):
        package = super(Package, cls).__new__(cls)
        package.__init__(*args, **kwargs)
        return contextlib.closing(package)

и этот конструктор наследуется, так что вы можете просто наследовать, например

class SubPackage(Package):
    def close(self):
        pass

Я не думаю, что это возможно, например, члены должны быть удалены до __del__ называется. Я предполагаю, что причина вашего конкретного AttributeError находится где-то еще (возможно, вы ошибочно удаляете себя.файл в другом месте).

однако, как указывали другие, вы должны избегать использования __del__. Основная причина этого заключается в том, что экземпляры с __del__ не будет собираться мусор (они будут освобождены только тогда, когда их refcount достигнет 0). Поэтому, если ваш экземпляры участвуют в циклических ссылках, они будут жить в памяти до тех пор, пока приложение запускается. (Я могу ошибаться во всем этом, хотя мне придется снова прочитать документы gc, но я уверен, что это работает так).

стандартный способ-использовать atexit.register:

# package.py
import atexit
import os

class Package:
    def __init__(self):
        self.files = []
        atexit.register(self.cleanup)

    def cleanup(self):
        print("Running cleanup...")
        for file in self.files:
            print("Unlinking file: {}".format(file))
            # os.unlink(file)

но вы должны иметь в виду, что это будет сохраняться все созданные экземпляры Package пока Python не будет завершен.

демо, используя код выше сохраненный как package.py:

$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...

Я думаю, что проблема может быть в __init__ Если есть больше кода, чем показано?

__del__ будет вызываться даже тогда, когда __init__ не был выполнен должным образом или вызвал исключение.

источник

просто оберните деструктор с помощью оператора try/except, и он не будет выдавать исключение, если ваши глобалы уже удалены.

Edit

попробуйте это:

from weakref import proxy

class MyList(list): pass

class Package:
    def __init__(self):
        self.__del__.im_func.files = MyList([1,2,3,4])
        self.files = proxy(self.__del__.im_func.files)

    def __del__(self):
        print self.__del__.im_func.files

он будет заполнять список файлов в del функция, которая гарантированно существует во время вызова. Прокси-сервер weakref должен предотвратить Python или себя от удаления себя.файлы переменной каким-то образом (если он будет удален, то это не повлияет на оригинал список файлов.) Если это не так, что это удаляется, даже если есть больше ссылок на переменную, то вы можете удалить инкапсуляцию прокси.

Comments

    Ничего не найдено.