Уведомления

Группа в Telegram: @pythonsu

#1 Июль 15, 2018 21:05:05

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

Добрый вечер. Интересно, как обстоят дела с предсказуемостью вызова __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]

насколько можно полагаться на такое поведение?

Офлайн

#2 Июль 15, 2018 21:21:22

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

надежность исполнения операций в __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 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]

Офлайн

#3 Июль 15, 2018 22:17:00

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

Levitanus
насколько можно полагаться на такое поведение?
Совсем нельзя полагаться. Вот если в спецификации языка будет написано тогда можно будет полагаться.



Офлайн

#4 Июль 15, 2018 22:36:55

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

спасибо

Офлайн

#5 Июль 15, 2018 22:51:30

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

Levitanus
Я так понимаю, что объект попадает в сборщик сразу как исчезает его референс (локальная переменная).
Это не совсем так, у каждого обьекта есть счетчик, который хранит количество ссылок на объект. Когда “ исчезает его референс ” счетчик становиться меньше на единицу. Метод же будет вызван, только когда счётчик ссылок достигнет нуля.
Не гарантируется, что метод будет вызван для объектов, всё ещё существующих на момент выхода интерпретатора.
И да существуют обстоятельства, которые могут помешать счётчику дойти до нуля, както циклические ссылки или ссылка на объект в фрейме стека при возбуждении исключения.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Офлайн

#6 Июль 15, 2018 23:40:25

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

Можно тогда здесь же спрошу? Чего-то не могу придумать:
(пакет - API для генерации кода на более примитивном языке. По сути, классы пакета транслируются в исходники другого ЯП, а вокруг “препроцессор” уже пишется на нормальном питоне.)

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

Я вот читаю, и понимаю, что звучит как жопа в архитектуре. Но решать задачу статическим анализом очень не хочу. Может я не прав, но мне кажется, не тот случай, и изначальная идея - использовать чистый исполняемый питон.

Офлайн

#7 Июль 15, 2018 23:56:28

Levitanus
Зарегистрирован: 2018-05-01
Сообщения: 46
Репутация: +  0  -
Профиль   Отправить e-mail  

надежность исполнения операций в __del__

ок, решил упростить задачу через дополнительный аргумент out и проверкой на возвращение None

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version