Levitanus
Июль 15, 2018 21:05:05
Добрый вечер. Интересно, как обстоят дела с предсказуемостью вызова __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
Июль 15, 2018 21:21:22
чуть модифицированная версия:
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
Июль 15, 2018 22:17:00
Levitanus
насколько можно полагаться на такое поведение?
Совсем нельзя полагаться. Вот если в спецификации языка будет написано тогда можно будет полагаться.
PEHDOM
Июль 15, 2018 22:51:30
Levitanus
Я так понимаю, что объект попадает в сборщик сразу как исчезает его референс (локальная переменная).
Это не совсем так, у каждого обьекта есть счетчик, который хранит количество ссылок на объект. Когда “ исчезает его референс ” счетчик становиться меньше на единицу. Метод же будет вызван, только когда счётчик ссылок достигнет нуля.
Не гарантируется, что метод будет вызван для объектов, всё ещё существующих на момент выхода интерпретатора.
И да существуют обстоятельства, которые могут помешать счётчику дойти до нуля, както циклические ссылки или ссылка на объект в фрейме стека при возбуждении исключения.
Levitanus
Июль 15, 2018 23:40:25
Можно тогда здесь же спрошу? Чего-то не могу придумать:
(пакет - API для генерации кода на более примитивном языке. По сути, классы пакета транслируются в исходники другого ЯП, а вокруг “препроцессор” уже пишется на нормальном питоне.)
У меня функция в декораторе может возвращать объект из самопального стека (в силу специфики задачи, стек должен быть простым как пробка).
В момент вызова функции создается новый фрейм, в который спихиваются все локали и return, если есть.
В момент выхода, по идее, надо либо держать фрейм до тех пор, пока на него есть референс (что я и хотел сделать через __del__), либо делать pop() прямо в декораторе (по сути, там просто счетчик указателя на ячейку массива стартовых элементов фреймов откатывается на1).
Так вот во втором случае, если использовать объект стека сам по себе, а не класть его значение внутрь “глобальных” классов, можно столкнуться с тем, что в стек уже на место даты искомого объекта что-то попало.
Как бы так запретить его использовать где либо, кроме инстанций “глобальных классов”.
Я вот читаю, и понимаю, что звучит как жопа в архитектуре. Но решать задачу статическим анализом очень не хочу. Может я не прав, но мне кажется, не тот случай, и изначальная идея - использовать чистый исполняемый питон.
Levitanus
Июль 15, 2018 23:56:28
ок, решил упростить задачу через дополнительный аргумент out и проверкой на возвращение None