2016-02-18 2 views
8

Я читал о различных способах очистки объектов в Python, и я наткнулся на эти вопросы (1, 2), которые в основном говорят, что очистка с использованием __del__() ненадежно и следующий код следует избегать:Является ли полагаться на __del __() для очистки в python ненадежной?

def __init__(self): 
    rc.open() 

def __del__(self): 
    rc.close() 

Проблема в том, что я использую именно этот код, и я не могу воспроизвести ни одну из проблем, упомянутых в вышеприведенных вопросах. Что касается моих знаний, я не могу пойти на альтернативу с помощью инструкции with, так как я предоставляю модуль Python для программного обеспечения с закрытым исходным кодом (testIDEA, кто-нибудь?). Это программное обеспечение создаст экземпляры определенных классов и избавит их, эти экземпляры должны быть готовы предоставлять услуги между ними. Единственная альтернатива __del__(), которую я вижу, - это вручную вызвать open() и close() по мере необходимости, что, я полагаю, будет довольно подверженным ошибкам.

Я понимаю, что когда я закрою интерпретатор, нет гарантии, что мои объекты будут уничтожены правильно (и это меня не сильно беспокоит, черт возьми, даже авторы Python решили, что все в порядке). Кроме того, я играю с огнем, используя __del__() для очистки?

PS: Я изначально думал о размещении наброска на мета вопросов о ошибках, которые невозможно воспроизвести, но понял, что я не получу технических деталей, которые мне нужны именно так.

+0

Выполняется ли '__exit__' даже при закрытии интерпретатора ...? –

+0

Также: я нашел [эту ссылку] (http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python), чтобы быть очень полезной по этому вопросу. –

+0

@RickTeachey вы ошиблись '__del__' за' __exit__', или вы предлагаете мне использовать '__exit__' в моем коде? Кроме того, я могу терпеть проблемы при закрытии. –

ответ

3

Вы наблюдаете типичную проблему с финализаторами в сборе мусора языков. Java имеет это, у C# есть это, и все они предоставляют метод очистки на основе области, такой как ключевое слово Python with, чтобы справиться с ним.

Основная проблема заключается в том, что сборщик мусора несет ответственность за очистку и уничтожение объектов. В C++ объект уничтожается, когда он выходит из области видимости, поэтому вы можете использовать RAII и иметь четко определенную семантику. В Python объект выходит из сферы действия и живет до тех пор, пока GC нравится. В зависимости от реализации Python это может быть другим.CPython с его GC, основанный на пересчете, является довольно мягким (поэтому вы редко видите проблемы), в то время как PyPy, IronPython и Jython могут сохранить объект в течение очень долгого времени.

Например:

def bad_code(filename): 
    return open(filename, 'r').read() 

for i in xrange(10000): 
    bad_code('some_file.txt') 

bad_code просачивается описатель файла. В CPython это не имеет значения. Сумма возврата упадет до нуля и сразу же удаляется. В PyPy или IronPython вы можете получить IOErrors или подобные проблемы, поскольку вы исчерпываете все доступные файловые дескрипторы (до ulimit на Unix или 509 ручках в Windows).

Очистка с помощью контекстного меню с менеджером контекста и with предпочтительнее, если вам необходимо гарантировать очистку. Вы точно знаете, когда ваши объекты будут завершены. Но иногда вы не можете легко применять эту очищенную область. То, когда вы можете использовать __del__, atexit или аналогичные конструкции, чтобы приложить максимум усилий при очистке. Это ненадежно, но лучше, чем ничего.

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

+0

Спасибо. Я действительно полагаюсь на более эффективную очистку в моем коде. Не могли бы вы пояснить, что гарантия CPython в вашем примере? Что есть не более одной открытой ручки? Не более 10? Что-то другое? –

+1

CPython использует систему подсчета ссылок для обработки очистки. Таким образом, в приведенном выше примере дескриптор выходит за пределы области видимости, что уменьшает коэффициент пересчета и сразу очищается, поэтому при запуске функции открывается только один дескриптор. – schlenk

+0

Этого достаточно для меня. Я могу уйти с требованием для конкретного интерпретатора Python для запуска моего кода. –

2

Есть несколько проблем с использованием __del__ для запуска кода.

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

Двое, существует множество случаев, когда __del__ никогда не будет запущен. Было ли исключение, когда объекты инициализировались и создавались? Вышел ли переводчик? Есть где-то круговая ссылка? Да, много, которые могут пойти не так, как надо, и очень немногие пути, чтобы чисто и последовательно справляться с этим.

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

Если вы действительно хотите запускать код, есть гораздо более эффективные механизмы - менеджеры контекста, сигналы/слоты, событие и т.д.

+0

Не могли бы вы рассказать об исключениях в __init__? Должен ли я поймать и реконструировать? Я знаю, что «с» - это путь, но я должен предоставить объект закрытому вызывающему объекту, поэтому мои параметры кажутся ограниченными –

+0

Если вы создаете или делаете что-то в своем '__init__', и во время '__init__', он не будет завершен, и ссылка на этот класс никогда не будет создана, поэтому' __del__' никогда не будет вызван, чтобы срывать или очищать все, что вы создали до возникновения ошибки. Каковы спецификации для объекта, который вы должны предоставить? Можете ли вы указать мне документы для закрытой исходной библиотеки? –

+0

Здесь есть [эта страница] (http://www.isystem.com/downloads/testIDEA/help/), в которой есть информация, если вы хотите посмотреть. Соответствующий раздел - Задачи-> Написание сценариев. –

 Смежные вопросы

  • Нет связанных вопросов^_^