Найти - Пользователи
Полная версия: надежность исполнения операций в __del__
Начало » Python для новичков » надежность исполнения операций в __del__
1
Levitanus
Добрый вечер. Интересно, как обстоят дела с предсказуемостью вызова __del__ в однопоточном скрипте без каллбеков (ну в смысле, запускается, считает, и выключается).

Я так понимаю, что объект попадает в сборщик сразу как исчезает его референс (локальная переменная).
Вот такой тест у меня стабильно в разных вариантах выдает одинаковую логику вызова метода __del__:
 class Return:
    count = 0
    def __init__(self):
        Return.count += 1
        self.count = Return.count
        print(f'{self} init')
    def __str__(self):
        return f'Return object {self.count}'
    def __del__(self):
        print(f'{self} delete')
def foo():
    print('inside foo')
    return Return()
def test():
    print('inside test')
    print('calling foo to x')
    x = foo()
    print('x is ', x)
    print('calling foo to print:', foo(), sep='\n')
    print('test end')
    return
print('start')
test()
print('end')

вывод:
start
inside test
calling foo to x
inside foo
Return object 1 init
x is Return object 1
inside foo
Return object 2 init
calling foo to print:
Return object 2
Return object 2 delete
test end
Return object 1 delete
end
[Finished in 0.2s]

насколько можно полагаться на такое поведение?
Levitanus
чуть модифицированная версия:
 class Return:
    count = 0
    def __init__(self):
        Return.count += 1
        self.count = Return.count
        print(f'{self} init')
    def __str__(self):
        return f'Return object {self.count}'
    def __del__(self):
        print(f'{self} delete')
def foo():
    print('inside foo')
    return Return()
def test():
    print('inside test')
    print('calling foo to x')
    x = foo()
    print('x is ', x)
    print('calling foo to print:', foo(), sep='\n')
    print('test end')
    return x
print('start')
print('calling test to module_x')
module_x = test()
print('module_x is ', module_x)
print('\ncalling test to nowhere')
test()
print('deleting module_x')
del(module_x)
print('end')

вывод:
start
calling test to module_x
inside test
calling foo to x
inside foo
Return object 1 init
x is Return object 1
inside foo
Return object 2 init
calling foo to print:
Return object 2
Return object 2 delete
test end
module_x is Return object 1

calling test to nowhere
inside test
calling foo to x
inside foo
Return object 3 init
x is Return object 3
inside foo
Return object 4 init
calling foo to print:
Return object 4
Return object 4 delete
test end
Return object 3 delete
deleting module_x
Return object 1 delete
end
[Finished in 0.1s]
doza_and
Levitanus
насколько можно полагаться на такое поведение?
Совсем нельзя полагаться. Вот если в спецификации языка будет написано тогда можно будет полагаться.
Levitanus
спасибо
PEHDOM
Levitanus
Я так понимаю, что объект попадает в сборщик сразу как исчезает его референс (локальная переменная).
Это не совсем так, у каждого обьекта есть счетчик, который хранит количество ссылок на объект. Когда “ исчезает его референс ” счетчик становиться меньше на единицу. Метод же будет вызван, только когда счётчик ссылок достигнет нуля.
Не гарантируется, что метод будет вызван для объектов, всё ещё существующих на момент выхода интерпретатора.
И да существуют обстоятельства, которые могут помешать счётчику дойти до нуля, както циклические ссылки или ссылка на объект в фрейме стека при возбуждении исключения.
Levitanus
Можно тогда здесь же спрошу? Чего-то не могу придумать:
(пакет - API для генерации кода на более примитивном языке. По сути, классы пакета транслируются в исходники другого ЯП, а вокруг “препроцессор” уже пишется на нормальном питоне.)

У меня функция в декораторе может возвращать объект из самопального стека (в силу специфики задачи, стек должен быть простым как пробка).
В момент вызова функции создается новый фрейм, в который спихиваются все локали и return, если есть.
В момент выхода, по идее, надо либо держать фрейм до тех пор, пока на него есть референс (что я и хотел сделать через __del__), либо делать pop() прямо в декораторе (по сути, там просто счетчик указателя на ячейку массива стартовых элементов фреймов откатывается на1).
Так вот во втором случае, если использовать объект стека сам по себе, а не класть его значение внутрь “глобальных” классов, можно столкнуться с тем, что в стек уже на место даты искомого объекта что-то попало.
Как бы так запретить его использовать где либо, кроме инстанций “глобальных классов”.

Я вот читаю, и понимаю, что звучит как жопа в архитектуре. Но решать задачу статическим анализом очень не хочу. Может я не прав, но мне кажется, не тот случай, и изначальная идея - использовать чистый исполняемый питон.
Levitanus
ок, решил упростить задачу через дополнительный аргумент out и проверкой на возвращение None
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB