3.4. Создание экземпляров классов

Создать экземпляр класса очень просто: достаточно вызвать класс, как если бы он был функцией, передав аргменты, определенные в методе __init__. Возвращаемое значение и есть созданный объект.

Пример 3.9. Создание экземпляра класса FileInfo

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 1
>>> f.__class__                                        2
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          3
'base class for file info'
>>> f                                                  4
{'name': '/music/_singles/kairo.mp3'}
1 Мы создаем экземпляр класса FileInfo (определенного в модуле fileinfo) и присваиваем его переменной f. Параметр "/music/_singles/kairo.mp3" в итоге будет передан в качестве аргумента filename методу __init__ класса FileInfo.
2 У каждого экземпляра класса есть специальный атрибут __class__, ссылающийся на его класс (обратите внимание, что его представление содержит физический адрес объекта-класса на моей машине, в вашем случае представление будет немного другим). Программисты на Java могут быть знакомы с классом Class, у которого есть такие методы, как getName и getSuperclass для получения информации о классе. В языке Python информация такого рода доступна непосредственно через специальные атрибуты __class__, __name__ и __bases__.
3 Вы можете получить строку документации экземпляра точно так же, как для функции или модуля. Все экземпляры одного класса имеют одну и ту же строку документации.
4 Помните, что метод __init__ сохраняет значение аргумента filename в self["name"]? Вот и результат. Аргумент, который мы передали при создании экземпляра передается методу __init__ (плюс ссылка на объект self, которую Python добавляет сам).
Замечание
В языке Python для создания нового экземпляра необходимо просто вызвать класс, как если бы он был функцией. Здесь нет оператора new, как в C++ и Java.

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

Пример 3.10. Попытка реализовать утечку памяти

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 1
...     
>>> for i in range(100):
...     leakmem()                                          2
1 На каждый вызов функции leakmem мы создаем экземпляр класса FileInfo и присваиваем его переменной f, локальной для этой функции. На этом функция заканчивается без явного уничтожения f, так что вы можете ожидать утечки памяти, но вы не правы. При выходе из функции локальная переменная f выходит из области видимости. С этого момента больше нет ссылок на созданный экземпляр класса FileInfo (мы больше нигде его не сохраняли, кроме f), так что Python уничтожает экземпляр за нас.
2 Сколько бы мы не вызывали функцию leakmem, утечки памяти не будет, потому что каждый раз перед выходом из leakmem Python будет удалять вновь созданный экземпляр FileInfo.

Технический термин для этого вида сборки мусора — “подсчет ссылок”. Python всегда знает количество ссылок на каждый созданный объект. В приведенном примере была единственная ссылка — локальная переменная f. При выходе из функции переменная f выходит из области видимости, количество ссылок становится равным нулю и Python автоматически уничтожает экземпляр.

Бывают ситуации, когда подсчет ссылок не дает нужного результата. Если вы создаете объекты, ссылающиеся друг на друга (например, двусвязные списки, в которых каждый элемент имеет ссылку на следующий и предыдущий элемент в списке), ни один из объектов не может быть уничтожен, так как счетчик ссылок не достигает нуля. Начиная с версии 2.0 в языке Python есть дополнительный способ сборки мусора (“mark-and-sweep”), достаточно сообразительный, чтобы работать с такими ситуациями и корректно разрывать циклические ссылки.

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

Дополнительная литература