Конструкторы и деструкторы python

Обновлено: 28.03.2024

I do things mostly in C++, where the destructor method is really meant for destruction of an acquired resource. Recently I started with python (which is really a fun and fantastic), and I came to learn it has GC like java. Thus, there is no heavy emphasis on object ownership (construction and destruction).

As far as I've learned, the __init__() method makes more sense to me in python than it does for ruby too, but the __del__() method, do we really need to implement this built-in function in our class? Will my class lack something if I miss __del__() ? The one scenario I could see __del__() useful is, if I want to log something when destroying an object. Is there anything other than this?

You almost never want to implement __del__ . Classes that provide a custom __del__ have to be dealt with in a special way by the GC when dealing with cycles (because __del__ must be called exactly once) and in some cases the GC will not be able to collect them [though from python3.4+ this has been improved quite a lot]. There are very few cases in which it is a good idea to implement it.

If you're a beginner at python you should skip the __del__ function until way later. It's really rarely needed.

4 Answers 4

In the Python 3 docs the developers have now made clear that destructor is in fact not the appropriate name for the method __del__ .

object.__del__(self)

Called when the instance is about to be destroyed. This is also called a finalizer or (improperly) a destructor.

Note that the OLD Python 3 docs used to suggest that 'destructor' was the proper name:

object.__del__(self)

Called when the instance is about to be destroyed. This is also called a destructor. If a base class has a __del__() method, the derived class’s __del__() method, if any, must explicitly call it to ensure proper deletion of the base class part of the instance.

In a language with an automatic garbage collection mechanism, it would be difficult to deterministically ensure the invocation of a destructor, and hence these languages are generally considered unsuitable for RAII [Resource Acquisition Is Initialization]

So you should almost never be implementing __del__ , but it gives you the opportunity to do so in some (rare?) use cases

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

Введение в множественное наследование и super()

Введение в множественное наследование и super() для Python-разработчиков. Также в этой статье мы рассмотрим проблему алмаза.

Классы в Python

Рассматриваем, что собой представляют классы в Python и как их создавать. Кроме того, мы также разберем, что такое атрибуты и методы класса.

Деструктор в Python: уничтожение объектов

В этой статье мы разберем, как создать деструктор в Python, а также обсудим использование метода __del__() и работу деструктора.

Переменные класса и экземпляра в Python

В этой статье мы расскажем о переменных класса и экземпляра класса в Python. Мы разберем, чем они отличаются и как с ними работать.

pythonist cover delegating

Процесс делегирования в Python

Делегирование – это объектно-ориентированный подход, также называемый паттерном проектирования. Предположим, у вас есть объект x, и вы хотите изменить поведение только одного из его методов. Вы можете создать новый класс, …

pythonist cover oop5

Введение в объектно-ориентированное программирование: наследование

pythonist cover oop4

Введение в объектно-ориентированное программирование: класс Blob и модульность

Продолжаем изучать объектно-ориентированное программирование в Python. В этой статье разбираем вопрос модульности и удобства использования кода.

pythonist cover oop3

Введение в объектно-ориентированное программирование: много клякс

В этой статье мы собираемся изучить некоторые особенности использования нашего объекта.

pythonist cover oop 2

Введение в объектно-ориентированное программирование: создание среды для нашего объекта с Pygame

Продолжение серии статей про объектно-ориентировнаное программирование в Python. В данной статье при помощи модуля Pygame создаем игровое окружение.

Деструктор в Python – это специальный метод, который вызывается при уничтожении объекта. Конструктор же, наоборот, используется для создания и инициализации объекта класса.

В этой статье мы разберем:

  • как создать деструктор в Python
  • использование метода __del__()
  • как работает деструктор.

Что такое деструктор в Python?

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

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

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

Деструктор противоположен конструктору. Конструктор используется для инициализации объектов, а деструктор — для удаления или уничтожения объектов, в результате чего освобождается ресурс, занятый этими объектами.

В Python деструктор вызывается не вручную, а полностью автоматически. Это происходит в следующих двух случаях:

  • когда объект выходит за пределы области видимости
  • когда счетчик ссылок на объект достигает 0.

Для определения деструктора используется специальный метод __del__() . Например, когда мы выполняем del имя_объекта , деструктор вызывается автоматически, и объект собирается в мусор.


Деструктор в Python для уничтожения объектов

Создание деструктора с помощью метода __del__()

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

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

Синтаксис объявления деструктора будет следующим:

  • def – ключевое слово, которое используется для определения метода.
  • __del__() – зарезервированный метод. Он вызывается, как только все ссылки на объект будут удалены.
  • self : первый аргумент self относится к текущему объекту.

Примечание. Аргументы метода __del__() необязательны. Мы можем определить деструктор с любым количеством аргументов.

Пример

Давайте рассмотрим создание деструктора в Python на простом примере. Мы создадим класс Student с деструктором.

Запустим наш код и получим следующий результат:

Примечание. Как видно из вывода, при удалении ссылки на объект с помощью del s1 метод __del__() вызывается автоматически.

В приведенном выше коде мы создали один объект. s1 – это ссылочная переменная, указывающая на вновь созданный объект.

Деструктор вызывается, когда ссылка на объект удалена или счетчик ссылок на объект доходит до нуля.

python logo

Английский для программистов

Наш телеграм канал с тестами по английскому языку для программистов. Английский это часть карьеры программиста. Поэтому полезно заняться им уже сейчас

Важные моменты, которые следует помнить о деструкторе

  • Метод __del__() вызывается для любого объекта, когда счетчик ссылок для этого объекта становится равным нулю.
  • Счетчик ссылок для данного объекта становится нулевым, когда работа программы завершается, или мы удаляем все ссылки вручную с помощью ключевого слова del .
  • Деструктор не будет запускаться при удалении какой-то одной ссылки на объект. Он будет вызываться только тогда, когда все ссылки на объект будут удалены.


Работа деструктора

Пример

Давайте разберемся в приведенных выше пунктах на примере.

Сначала создадим объект класса Student , используя s1 = student ('Emma') .

Затем давайте создадим новую ссылку на объект, присвоив переменной s2 значение s1 (т.е. s2 = s1 ).

Теперь обе ссылочные переменные s1 и s2 указывают на один и тот же объект.

От редакции Pythonist. Ссылки, имена и значения подробно рассмотрены в статье «Факты и мифы об именах и значениях в Python».

Далее мы удалим ссылку s1 .

Затем добавим 5 секунд задержки (sleep) к основному потоку, чтобы было ясно, что деструкторы вызываются только при удалении всех ссылок на объекты.

  • Как вы можете видеть из полученного результата, деструкторы вызываются только тогда, когда удаляются все ссылки на объекты.
  • Кроме того, деструктор выполняется, когда код (программа) заканчивается и объект становится доступным для сборщика мусора. Например, мы не удаляли ссылку на объект s2 вручную с помощью del s2 . Это произошло автоматически, т.к. программа закончилась.

Случаи, когда деструктор работает не корректно

__del__() не является идеальным решением для очистки ненужных объектов. В Python деструктор ведет себя странно и не выполняется в следующих двух случаях:

  • ссылка является круговой: два объекта ссылаются друг на друга
  • исключение в методе __init__()

Круговая ссылка

__del__() некорректно работает в случае циклической ссылки.Суть этой ссылки в том, что два объекта ссылаются друг на друга. И когда оба объекта выходят за пределы области видимости, Python не знает, какой объект уничтожить первым. Поэтому, чтобы избежать ошибок, он не уничтожает ни один из них.

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

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

Пример

В приведенном ниже примере в идеале оба объекта — Vehicle и Car — должны быть уничтожены сборщиком мусора после выхода за пределы области видимости. Тем не менее, из-за циклической ссылки они остаются в памяти.

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

Запустим наш код и получим следующее:

Исключение в методе __init__()

В объектно-ориентированном программировании конструктор – это специальный метод, используемый для создания и инициализации объекта класса. Используя метод __init__() , мы можем реализовать конструктор для инициализации объекта.

В ООП, если в конструкторе возникает какое-либо исключение при инициализации объекта, конструктор уничтожает объект.

Аналогично, в Python, если в методе инициализации возникает какое-либо исключение при инициализации объекта, вызывается метод del . Но на самом деле объект не создается, и ему не выделяются ресурсы.

Несмотря на то, что объект так и не был инициализирован правильно, метод del все равно попытается очистить все ресурсы. А это, в свою очередь, может привести к другому исключению.

Пример

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

Заключение

В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта.

Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных.

Для выполнения задачи очистки перед удалением объекта в Python мы используем метод __del__() .

При удалении ссылки на объект деструктор не запускается. Для этого нужно удалить все ссылки на этот объект.

python logo

Английский для программистов

Наш телеграм канал с тестами по английскому языку для программистов. Английский это часть карьеры программиста. Поэтому полезно заняться им уже сейчас

Согласно Алану Кэю — автору языка программирования Smalltalk — объектно-ориентированным может называться язык, построенный с учетом следующих принципов [1] :

Для определения класса используется оператор class :

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

Минимально возможное определение класса выглядит так:

В терминологии Python члены класса называются атрибутами, функции класса — методами, а поля класса — свойствами (или просто атрибутами).

Определения методов аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по общепринятому соглашению self :

Определения атрибутов — это обычные операторы присваивания, которые связывают некоторые значения с именами атрибутов.

В языке Python класс не является чем-то статическим, поэтому добавить атрибуты можно и после определения:

Для создания объекта — экземпляра класса (то есть, инстанцирования класса), достаточно вызвать класс по имени и задать параметры конструктора:

Переопределив классовый метод __new__ , можно управлять процессом создания экземпляра. Этот метод вызывается до метода __init__ и должен вернуть новый экземпляр либо None (в последнем случае будет вызван __new__ родительского класса). Метод __new__ используется для управления созданием неизменчивых (immutable) объектов, управления созданием объектов в случаях, когда __init__ не вызывается, например, при десериализации (unpickle). Следующий код демонстрирует один из вариантов реализации шаблона Одиночка:

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

Следующий класс имеет конструктор, инициализатор и деструктор:

В момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно «истощённой»Шаблон:Что, поэтому в деструкторе следует делать только самое необходимое. Кроме того, не обработанные в деструкторе исключения игнорируются.

Обычно время жизни объекта, определённого в программе на Python, не выходит за рамки времени выполнения процесса этой программы.

Для преодоления этого ограничения объект можно сохранить, а после — восстановить. Как правило, при записи объекта производится его сериализация, а при чтении — десериализация.

Инкапсуляция является одним из ключевых понятий ООП. Все значения в Python являются объектами, инкапсулирующими код (методы) и данные и предоставляющими пользователям общедоступный интерфейс. Методы и данные объекта доступны через его атрибуты.

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

Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких языках как C++ или Java: атрибут остается доступным, но под именем вида _ИмяКласса__ИмяАтрибута , а при каждом обращении Python будет модифицировать имя в зависимости от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний классы могут иметь атрибут с именем, например, «__f», но не будут мешать друг другу.

Особым случаем является наличие двух подчеркиваний в начале и в конце имени атрибута. Они используются для специальных свойств и функций класса (например, для перегрузки операции). Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных атрибутов, изменяющих поведение объекта.

Доступ к атрибуту может быть как прямой:

Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута:

Разумеется, первый способ хорош только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.

Существуют два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке методов __getattr__() , __setattr__() , __delattr__() , а второй — метода __getattribute__() . Второй метод помогает управлять чтением уже существующих атрибутов.

Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы RPC для Python, имитируя методы и свойства, реально существующие на удаленном сервере.

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

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

В общем случае для получения класса-предка применяется функция super .

Используя специально предусмотренное исключение NotImplementedError , можно имитировать чисто виртуальные методы:

Или, с использованием декоратора, так:

Изменяя атрибут __class__ , можно перемещать объект вверх или вниз по иерархии наследования (впрочем, как и к любому другому типу)

Однако, в этом случае никакие преобразования типов не делаются, поэтому забота о согласованности данных всецело лежит на программисте. Кроме того, присваивание атрибуту __class__ не должно применяться по поводу и без. Прежде чем решиться на его использование, необходимо рассмотреть менее радикальные варианты реализации изменения объекта, то есть по сути шаблона проектирования State.

Более того, полиморфизм в Python вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorphism) [2] . Например, чтобы экземпляру класса «прикинуться» файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно .read() , .readlines() , .close() и т. п.).

Встроенные типы и их методы имеют синтаксическую поддержку в языке Python или другие особые «привилегии». Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.

Воспользоваться точно такой же синтаксической поддержкой может и любой определённый пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример — переопределить функцию:

Аналогично поддаются переопределению все операции встроенных типов. Ещё один пример связан с вычислением длины объекта с помощью функции len() . Эта встроенная функция вызывает специальный метод:

Методы __getitem__,__setitem__,__delitem__,__contains__ позволяют создать интерфейс для словаря или списка( dict ).

Достаточно просто переопределить и числовые типы. Скажем, следующий класс использует инфиксную операцию * :

Последний из методов — .__str__() — отвечает за представление экземпляра класса при печати оператором print и в других подобных случаях.

Аналогичные методы имеются и у соответствующих встроенных типов:

Не все из них существуют на самом деле: большая часть имитируется интерпретатором Python для удобства программиста. Такое поведение позволяет экономить время при наиболее важных операциях (например, сложение целых не приводит к поиску и вызову метода __add__ у класса int ) и память не расходуется на этот поиск и вызов, но приводит к невозможности изменения методов у встроенных классов.

При описании предметной области классы могут образовывать иерархию, в корне которой стоит базовый класс, а нижележащие классы (подклассы) наследуют свои атрибуты и методы, уточняя и расширяя поведение вышележащего класса (надкласса). Обычно принципом построения классификации является отношение «IS-A» («есть» — между экземпляром и классом) и «AKO» («a kind of» — «разновидность» — между классом и суперклассом) [3] .

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

В Python (из-за «утиной типизации») отсутствие наследования ещё не означает, что объект не может предоставлять тот же самый интерфейс.

Множественное наследование в Python применяется в основном для добавления примесей (mixins) — специальных классов, вносящих некоторую черту поведения или набор свойств [4] .

За достаточно простым в использовании механизмом доступа к атрибутам в w:Python кроется довольно сложный алгоритм. Далее будет приведена последовательность действий, производимых интерпретатором при разрешении запроса object.field (поиск прекращается после первого успешно завершённого шага, иначе происходит переход к следующему шагу).

  1. Если у object есть метод __getattribute__ , то он будет вызван с параметром 'field' (либо __setattr__ или __delattr__ в зависимости от действия над атрибутом)
  2. Если у object есть поле __dict__ , то ищется object.__dict__['field']
  3. Если у object.__class__ есть поле __slots__ , то 'field' ищется в object.__class__.__slots__
  4. Проверяется object.__class__.__dict__['fields']
  5. Производится рекурсивный поиск по __dict__ всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для «классических» и «новых» классов.
  6. Если у object есть метод __getattr__ , то вызывается он с параметром 'field'
  7. Вызывается исключение AttributeError .

Если поиск окончен успешно, то проверяется, является ли атрибут классом «нового стиля». Если является, то проверяется наличие у него метода __get__ (либо __set__ или __delete__ , в зависимости от действия над атрибутом), если метод найден, то происходит следующий вызов object.field.__get__(object) и возвращается его результат (такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) и используются, например, для создания свойств [5] ).

Эта последовательность распространяется только на пользовательские атрибуты. Системные атрибуты, такие как __dict__ , __len__ , __add__ и другие, имеющие специальные поля в С-структуре описания класса находятся сразу.

В версиях до 2.2 некоторые объектно-ориентированные возможности Python были заметно ограничены. Например, было невозможно наследовать встроенные классы и классы из модулей расширения. Свойства (property) не выделялись явно. Начиная с версии 2.2, объектная система Python была существенно переработана и дополнена. Однако для совместимости со старыми версиями Python было решено сделать две объектные модели: «классические» типы (полностью совместимые со старым кодом) и «новые» [6] . В версии Python3 поддержка «старых» классов будет удалена.

Для построения «нового» класса достаточно унаследовать его от другого «нового». Если нужно создать «чистый» класс, то можно унаследоваться от object — родительского типа для всех «новых» классов.

Все стандартные классы — классы «нового» типа. [7]

Агрегация, когда один объект входит в состав другого, или отношение «HAS-A» («имеет»), реализуется в Python с помощью ссылок. Python имеет несколько встроенных типов контейнеров: список, словарь, множество. Можно определить собственные классы контейнеров со своей логикой доступа к хранимым объектам. (Следует заметить, что в Python агрегацию можно считать разновидностью ассоциации, так реально объекты не вложены друг в друга в памяти и, более того, время жизни элемента может не зависеть от времени жизни контейнера.)

Следующий класс из модуля utils.py среды web.py является примером контейнера-словаря, дополненного возможностью доступа к значениям при помощи синтаксиса доступа к атрибутам:

Вот как он работает:

Для доступа к контейнерам очень удобно использовать итераторы:

Отношение использования («USE-A») экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают циклические ссылки. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Python с помощью механизма подсчета ссылок. Удалением таких объектов занимается сборщик мусора.

Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью слабых ссылок. Слабые ссылки не препятствуют удалению объекта.

Для работы со слабыми ссылками применяется модуль weakref .

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

При объявлении метакласса за основу можно взять класс type . Пример:

Разумеется, вместо оператора print код метакласса может выполнять более полезные функции: регистрировать класс, передавать действия с классами на удаленную систему, использовать классы для других целей (например, как декларации или ограничения) и т. п.

Синтаксис описания метода ничем не отличается от описания функции, разве что его положением внутри класса и характерным первым формальным параметром self , с помощью которого внутри метода можно ссылаться на сам экземпляр класса (название self является соглашением, которого придерживаются программисты на Python):

Статические методы в Python являются синтаксическими аналогами статических функций в основных языках программирования. Они не получают ни экземпляр ( self ), ни класс ( cls ) первым параметром. Для создания статического метода (только «новые» классы могут иметь статические методы) используется декоратор staticmethod

Статические методы реализованы с помощью свойств (property).

Классовые методы в Python занимают промежуточное положение между статическими и обычными. В то время как обычные методы получают первым параметром экземпляр класса, а статические не получают ничего, в классовые методы передается класс. Возможность создания классовых методов является одним из следствий того, что в Python классы также являются объектами. Для создания классового (только «новые» классы могут иметь классовые методы) метода можно использовать декоратор classmethod

Классовые методы достаточно часто используются для перегрузки конструктора. Классовые методы, как и статические, реализуются через свойства (property).

Примером для иллюстрации сути мультиметода может служить функция add() из модуля operator :

В языке Python достаточно легко реализовать и определённые пользователем мультиметоды [8] . Например, эмулировать мультиметоды можно с помощью модуля multimethods.py (из Gnosis Utils) :

Объекты всегда имеют своё представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или передавать их на другие компьютеры. Одним из решений этой проблемы является устойчивость объектов (англ. object persistence ) которая достигается с помощью хранения представлений объектов (сериализацией) в виде байтовых последовательностей и их последующего восстановления (десериализация).

Модуль pickle является наиболее простым способом «консервирования» объектов в Python.

Следующий пример показывает, как работает сериализация и десериализация:

Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже — прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить, что нельзя десериализовать данные из непроверенных источников с помощью модуля pickle , так как при этом возможны практически любые действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам или с ненадежными источниками можно воспользоваться другими модулями для сериализации.

В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта — это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы __getstate__ , __setstate__ и др.).

На стандартном для Python механизме сериализации построена работа модуля shelve (shelve (англ. глаг.) — ставить на полку; сдавать в архив). Модуль предоставляет функцию open . Объект, который она возвращает, работает аналогично словарю, но объекты сериализуются и сохраняются в файле:

Сериализация pickle — не единственная возможная, и подходит не всегда. Для сериализации, не зависящей от языка программирования, можно использовать, например, XML.

__del__(self) above fails with an AttributeError exception. I understand Python doesn't guarantee the existence of "global variables" (member data in this context?) when __del__() is invoked. If that is the case and this is the reason for the exception, how do I make sure the object destructs properly?

Reading what you linked, global variables going away doesn't seem to apply here unless you're talking about when you program is exiting, during which I guess according to what you linked it might be POSSIBLE that the os module itself is already gone. Otherwise, I don't think it applies to member variables in a __del__() method.

The exception is thrown long before my program exits. The AttributeError exception I get is Python saying it doesn't recognize self.files as being an attribute of Package. I may be getting this wrong, but if by "globals" they don't mean variables global to methods (but possibly local to class) then I don't know what causes this exception. Google hints Python reserves the right to clean up member data before __del__(self) is called.

The code as posted seems to work for me (with Python 2.5). Can you post the actual code that is failing - or a simplified (the simpler the better version that still causes the error?

@ wilhelmtell can you give a more concrete example? In all my tests, the del destructor works perfectly.

If anyone wants to know: This article elaborates why __del__ should not be used as the counterpart of __init__ . (I.e., it is not a "destructor" in the sense that __init__ is a constructor.

10 Answers 10

I'd recommend using Python's with statement for managing resources that need to be cleaned up. The problem with using an explicit close() statement is that you have to worry about people forgetting to call it at all or forgetting to place it in a finally block to prevent a resource leak when an exception occurs.

To use the with statement, create a class with the following methods:

In your example above, you'd use

Then, when someone wanted to use your class, they'd do the following:

The variable package_obj will be an instance of type Package (it's the value returned by the __enter__ method). Its __exit__ method will automatically be called, regardless of whether or not an exception occurs.

You could even take this approach a step further. In the example above, someone could still instantiate Package using its constructor without using the with clause. You don't want that to happen. You can fix this by creating a PackageResource class that defines the __enter__ and __exit__ methods. Then, the Package class would be defined strictly inside the __enter__ method and returned. That way, the caller never could instantiate the Package class without using a with statement:

Читайте также: