Задать вопрос
adugin
@adugin

Почему вылетает исключение при выносе кода из метода __del__ в change_extension()?

Есть процедура, которая возвращает имя файла с заменённым расширением, и в зависимости от флага rename может переименовать существующий файл:
def change_extension(file_name, new_extension, rename=False):
    new_file_name = os.extsep.join([os.path.splitext(file_name)[0], new_extension])
    if rename:
        if os.path.exists(new_file_name):
            os.remove(new_file_name)
        os.rename(file_name, new_file_name)
    return new_file_name

Есть класс, в методе __del__ которого используется точно такой же код, что и в процедуре выше. Метод __del__ срабатывает всегда без ошибок, если код переименования файла написан прямо в нём, и вызывает ошибку на последнем проходе, если вынести код в отдельную функцию change_extension():
class CSVWriter():
    # Initialize and setup dedicated CSV writer
    def __init__(self, file_name_xl3, output_folder, filter_name, filter_function):
        file_name = '.'.join([os.path.split(file_name_xl3)[1][:-4], filter_name, 'tmp'])
        self.file_name = os.path.join(output_folder, file_name)
        self.file_name_xl3 = file_name_xl3
        self.output_folder = output_folder
        self.file_handle = open(self.file_name, 'wb')
        self.filter_name = filter_name
        self.filter_function = filter_function
        self.writer = csv.writer(self.file_handle, dialect=csv.excel_tab, quoting=csv.QUOTE_NONE, delimiter=CFG_CSV_DELIMITER)
    # Automatically close output file in the end and change extension to 'csv'
    def __del__(self):
        self.file_handle.close()
        # change_extension(self.file_name, 'csv', rename=True)  # Why change_extension with the same code causes error?
        new_file_name = os.extsep.join([os.path.splitext(self.file_name)[0], 'csv'])
        if os.path.exists(new_file_name):
            os.remove(new_file_name)
        os.rename(self.file_name, new_file_name)
    # Write line to output CSV file if xdr filter has been passed
    def write_xdr(self, xdr, bypass=False):
        if bypass or self.filter_function(xdr):
            return self.writer.writerow(xdr)

Исключение в консоли:
Exception TypeError: "'NoneType' object is not callable" in <bound method CSVWri
ter.__del__ of <__main__.CSVWriter instance at 0x020565D0>> ignored
Exception TypeError: "'NoneType' object is not callable" in <bound method CSVWri
ter.__del__ of <__main__.CSVWriter instance at 0x020566C0>> ignored
Exception TypeError: "'NoneType' object is not callable" in <bound method CSVWri
ter.__del__ of <__main__.CSVWriter instance at 0x02056710>> ignored
Exception TypeError: "'NoneType' object is not callable" in <bound method CSVWri
ter.__del__ of <__main__.CSVWriter instance at 0x02056670>> ignored

В чём может быть проблема?
  • Вопрос задан
  • 214 просмотров
Подписаться 1 Оценить 1 комментарий
Решения вопроса 1
@nirvimel
Python не C++, в Python НЕТ деструкторов (в смысле C++), которые вызываются гарантированно. __del__ - не прямой аналог деструктора C++, его поведение зависит от GC, он может вызываться в любой момент, и в общем случае нет никакой гарантии что он вообще будет вызван. В общем, __del__ в Python - плохая практика.
Что же делать:
  1. Надо стараться реорганизовать код так, чтобы объект, требующий финаллизации, использовался через with(your_object). Надо следовать хорошим примерам, например, file object, работа с которым идет через with(open('workfile')).
  2. Если совсем не получается свести к логику кода к такой модели, то можно объявить метод close() и вызывать его вручную. Но если ваш код размазан так, что в него не получается вкрутить with, то нет гарантий что на какой-то ветке алгоритма вы забудете вызвать этот close().
  3. Худший вариант: оставить таки __del__. Нигде не вызвать его явно, просто бросать объекты после использования. По окончанию исполнения скрипта, GC подберет их и вызовет всем __del__, но только в том случае если между ними нет циклических ссылок, но в достаточно крупной программе это вообще невозможно гарантировать.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы