LINUX.ORG.RU

[python] циклические зависимости?

 


0

0

Есть такой код:

class Ancestor:
    def __init__(self):
        self.hello()

    def hello(self):
        print("hello!")

    def __del__(self):
        print("destructor")


class Child(Ancestor):
    def __init__(self):
        self.hello = self.myhello
        Ancestor.__init__(self)

    def myhello(self):
        print("this is my hello")


child = Child()
print("deleting instance")
del child

Так вот надписи «destructor» мы не увидим. Почему такая трава происходит? Как лечить?(делать del self.hello ?).

★★★★★

на ваш код интерпретатор выдал

Traceback (most recent call last):
  File "destruction.py", line 38, in <module>
    child = Child()
  File "destruction.py", line 35, in __init__
    self.hello = self.myhello
AttributeError: Child instance has no attribute 'myhello'
destructor

stave ★★★★★
()
Ответ на: комментарий от stave

Видимо, ты неправильно скопировал(проверь отступы, set paste в vim). И там всего 22 строки.

Чую щас отступоненавистники тред загадят :)

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

>Видимо, ты неправильно скопировал

QQ, и правда отступы :/ деструктора нет

stave ★★★★★
()

Как вариант можно в Child назвать метод hello или использовать weakref.

Но почему такое происходит я не понимаю.

Davidov ★★★★
()

В Питоне нет деструкторов. Умники, которые рассчитывают на Си++-поведение, напршиваются на неприятности.

> Как лечить?

Прогера? Мануальной терапией.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> В Питоне нет деструкторов

Угу-угу, tp_dealloc враги питона придумали.

Мне не вызов деструктора нужен, мне надо чтобы память вернулась.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

>> В Питоне нет деструкторов

> Угу-угу, tp_dealloc враги питона придумали.

Бгг. С каких пор детали реализации CPython явлются языком Python?

tailgunner ★★★★★
()
Ответ на: комментарий от true_admin

> Ок, смотри сюда:

Смотри сюда: "на Си++-поведение".

> http://docs.python.org/3.0/reference/datamodel.html?highlight=__del__#object... .

И вот сюда тоже посмотри: "del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x‘s reference count reaches zero.

...

Circular references which are garbage are detected when the option cycle detector is enabled (it’s on by default), but can only be cleaned up if there are no Python- level __del__() methods involved."

P.S. А tp_dealloc там просто нет.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Да я вообще не знаю про C++ ничего, мне не это нужно. Мне нужно чтобы память освобождалась. У меня демон на питоне, я хочу понять в каких местах можно накосячить. Я вот одно уже нашёл.

> if there are no Python- level __del__() methods involved.

Мде, получается что нельзя свои __del__ пихать? Я щас проверил, проблема не наблюдается если убрать свой __del__. Проверил путём export PYTHONDUMPREFS=1.

> P.S. А tp_dealloc там просто нет.

это ты к чему? Раз нету и и фиг с ним, про него в другом разделе пишут. Я вообще про внутренности PyObject и PyTypeObject начал говорить потому что по ним очень легко понять что на самом деле твориться в питоне. Пусть это конкретная реализация, но это реализация стандарта.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Я щас проверил, проблема не наблюдается если убрать свой __del__.

Афигеть, поведение соответствует документации %) Что же тебя не устраивает?

>> P.S. А tp_dealloc там просто нет.

> это ты к чему?

Ну ты зачем-то помянул его.

> Я вообще про внутренности PyObject и PyTypeObject начал говорить потому что по ним очень легко понять что на самом деле твориться в питоне.

Это детали реализации, на которые нельзя полагаться.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Афигеть, поведение соответствует документации %) Что же тебя не устраивает?

Поведение.
Я хочу деструктор. Конструктор-то есть, почему бы и деструктор нормальный не сделать.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> почему бы и деструктор нормальный не сделать.

Спроси об этом на python-dev.

А если тебе и правда нужен деструктор, разбивай циклы руками (как и сказано в доке).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

всё, нашёл описание всего этого безобразия.

У gc.garbage написано что к чему.

Блин, что-то питон в последние пол года меня тока разочаровывает...

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Я хочу деструктор. Конструктор-то есть, почему бы и деструктор нормальный не сделать.

А для чего тогда GC?

smh ★★★
()
Ответ на: комментарий от smh

ты выдрал мою фразу из контента. Тут речь идёт о том что gc не работает если в классе есть метод __del__.

В общем, программирование под питон сильно отличается от того что я о нём думал.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

>Я хочу деструктор.

От деструкторов одни проблемы. Даже к канонiчному С++ они привинчены криво: ошибку оттуда вернуть нельзя, экзепшен сконвертируется в terminate().

Absurd ★★★
()
Ответ на: комментарий от true_admin

> ты выдрал мою фразу из контента. Тут речь идёт о том что gc не работает если в классе есть метод __del__.

Он работает. Просто вызовется этот метод только тогда, когда ссылок на объект больше не будет.

smh ★★★
()
Ответ на: комментарий от smh

>Просто вызовется этот метод только тогда, когда ссылок на объект больше не будет.

А по выходу из программы вызовется? Просто Жаба оставляет память as-is и finalize() не вызывает, например.

Absurd ★★★
()

hizel@night:~$ cat lor2.py
class Ancestor:
    def __init__(self):
        self.hello()

    def hello(self):
        print("hello!")

    def __del__(self):
        print("destructor")


class Child(Ancestor):
    def __init__(self):
        self.hello = self.myhello
        Ancestor.__init__(self)

    def myhello(self):
        print("this is my hello")


child = Child()
print("deleting instance")
del child.hello
del child

hizel@night:~$ python lor2.py 
this is my hello
deleting instance
destructor

-----

да и почему не написать так?

hizel@night:~$ cat lor2.py
class Ancestor:
    def __init__(self):
        self.hello()

    def hello(self):
        print("hello!")

    def __del__(self):
        print("destructor")


class Child(Ancestor):
    def __init__(self):
        Ancestor.__init__(self)

    def hello(self):
        print("this is my hello")


child = Child()
print("deleting instance")
del child

hizel@night:~$ python lor2.py 
this is my hello
deleting instance
destructor

hizel ★★★★★
()
Ответ на: комментарий от Absurd

> А по выходу из программы вызовется?

AFAIK нет.

smh ★★★
()
Ответ на: комментарий от Absurd

>Даже к канонiчному С++ они привинчены криво: ошибку оттуда вернуть нельзя, экзепшен сконвертируется в terminate().

Бедные нещасные питоноиды. Им даже в C++ деструкторами нельзя пользоваться. А я вот юзаю их и даже исключения из деструкторов кидаю и успешно ловлю.

legolegs ★★★★★
()
Ответ на: комментарий от tailgunner

>Даже при раскрутке стека?

Нет. А вам часто приходится? Может надо к проектированию иначе подходить?

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

>> Даже при раскрутке стека?

> Нет.

Ну так побереги свою жалость.

> Может надо к проектированию иначе подходить?

А... так ты _это_ проектированием считаешь, прикольно.

tailgunner ★★★★★
()
Ответ на: комментарий от legolegs

> А я вот юзаю их и даже исключения из деструкторов кидаю и успешно ловлю.

Можно на пальцах объяснить, где такие исключения уместны?

З.Ы. А неплохо бы компилятору статически проверять, что эти исключения не проявятся при раскрутке стэка.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от true_admin

> Чую щас отступоненавистники тред загадят

Мне отступы больше нравятся, чем скобки, но вот их хрупкость просто потрясает.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от hizel

hizel, я про это и писал. Не нравится мне это потому что у меня на самом деле задача совсем другая, я пишу tcp-сервер. Мне бы хотелось чтобы в деструкторе закрывалось соединение и в разные моменты метод on_recv указывал то на do_handshake, то на read_request(), то на terminate_connection().

Самое обидное то что модуль написан на C и я это ограничение GC даже не увижу т.к. tp_free/tp_dealloc будут всё равно вызваны.

PS 2excelion: изыди, плесень.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

> Мне отступы больше нравятся, чем скобки, но вот их хрупкость просто потрясает.

При том, что на практике ты ими не пользовался.

tailgunner ★★★★★
()
Ответ на: комментарий от true_admin

> Мне бы хотелось чтобы в деструкторе закрывалось соединение

Еще раз повторю - в Питоне нет деструкторов.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> При том, что на практике ты ими не пользовался.

Твоя оценка м.б. полезна читающим ветку, но не мне.

Ты лучше скажи, какова твоя оценка отступов.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>Можно на пальцах объяснить, где такие исключения уместны?

Исключения в деструкторах? Везде, где деструкторы выполняют полезную работу (т.е. делают что-то интереснее delete и записи в логи). Доверять ли деструктору полезную работу? Надо ответить на два вопроса: имеет ли смысл в случае сбоя пытаться проделать действие ещё раз? Насколько ужасно, если программист забудет вызвать отдельный метод (конструктор вызовется обязательно)?

>З.Ы. А неплохо бы компилятору статически проверять, что эти исключения не проявятся при раскрутке стэка.

Полагаю, его сообщение будет столь же малополезно, как и -Wunreachable-code: много и почти всегда не по делу. Например, iostream можно заставить генерировать исключения, а при размотке стека он попадётся с большой вероятностью.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

> Надо ответить на два вопроса: имеет ли смысл в случае сбоя пытаться проделать действие ещё раз? Насколько ужасно, если программист забудет вызвать отдельный метод (конструктор вызовется обязательно)?

Хотелось бы именно *пример*, когда это уместно или хотя бы просто хочется сделать (без кода, только постановка задачи).

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Например, обёртка над FILE*, проверяющая результат close()

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

> А я вот юзаю их и даже исключения из деструкторов кидаю и успешно ловлю.

Можно с этого места подробнее? Что ты имеешь ввиду? Есть непроложное правило: ни деструктор, ни функция swap не могут генерировать исключений (Герб Саттер). Иначе ломается C++, например, при работе с массивами...

dave ★★★★★
()
Ответ на: комментарий от tailgunner

> Еще раз повторю - в Питоне нет деструкторов.

ну ок, нету. Тогда по твоей логике программист никак не управляет теми объектами которые он порождает. Т.е. он не может быть уверен что он сможет нормально убить объект когда тот ему будет не нужен, так что ли?

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Тогда по твоей логике программист никак не управляет теми объектами которые он порождает.

Это не моя логика, не логика вообще, и просто не соответствует реальности.

> Т.е. он не может быть уверен что он сможет нормально убить объект когда тот ему будет не нужен, так что ли?

Примерно так. Практическое следствие для тебя - освобождать системные ресурсы нужно явным образом, не надеясь на сборщик мусора.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Практическое следствие для тебя - освобождать системные ресурсы нужно явным образом, не надеясь на сборщик мусора.

Вот я весь топик и пытаюсь про это узнать. Как это сделать? :).

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

>> Практическое следствие для тебя - освобождать системные ресурсы нужно явным образом, не надеясь на сборщик мусора.

> Вот я весь топик и пытаюсь про это узнать.

Да? O_o

> Как это сделать? :)

Ты даже задачу не сформулировал. Наверняка в твоем сервере есть момент, когда описатель клиента удаляется из массива активных клиентов - там и закрывай сокет (или что у тебя там).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Сформулирую вопрос по-другому.

Я не хочу чтобы сервер тёк памятью. Поэтому я должен уничтожать все объекты что я породил. Ты согласен с этим?

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Я не хочу чтобы сервер тёк памятью. Поэтому я должен уничтожать все объекты что я породил. Ты согласен с этим?

Нет. Ты должен их "забывать", убирая ссылки на них из долгоживущих структур.

tailgunner ★★★★★
()
Ответ на: комментарий от true_admin

> Почему бы мне не использовать __del__ для закрытия сокета?

Потому что момент вызова __del__ определен довольно туманно, и тебе придется самому следить за вещами типа циклических ссылок.

tailgunner ★★★★★
()
Ответ на: комментарий от dave

>Есть непроложное правило: ни деструктор, ни функция swap не могут генерировать исключений (Герб Саттер). Иначе ломается C++

Непреложные правила все записаны в стандарте языка и такого там нет.

legolegs ★★★★★
()
Ответ на: комментарий от tailgunner

так мне же полюбому надо следить за тем чтобы объекты удалялись, иначе память вся выжрется.

В общем, я тебя давно понял :). Ты как бы намекаешь что не стоит надеяться что будет вызван __del__. Только мне приходится заботится чтобы объекты таки уничтожались. А значит я могу его использовать при условии что я слежу за всеми ссылками на объект. Я правильно рассуждаю? :).

В реальности у меня есть метод self.on_close() в который я могу запихнуть код деструктора. Вернее, я def __del__() переименовал в def on_close().

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> так мне же полюбому надо следить за тем чтобы объекты удалялись, иначе память вся выжрется.

Нет. Тебе надо следить, чтобы ссылки на объекты не зависали в долгоживущих (глобальных) структурах.

> А значит я могу его использовать при условии что я слежу за всеми ссылками на объект. Я правильно рассуждаю? :).

Формально да. Но формально, можно бегать и по полю с граблями. Тем более, что особого обращения только объекты, существующие за пределами процесса.

tailgunner ★★★★★
()
Ответ на: комментарий от true_admin

>так мне же полюбому надо следить за тем чтобы объекты удалялись, иначе память вся выжрется.

Не выжрется, если не сохраняешь ссылку на объект ассоциированный с клиентом в долговременной структуре. Мне такое понадобилось только для асинхронного интерфейса работы с БД где по исходящему RPC слался запрос, а потом через входящий RPC получался ответ. Пришлось заводить отдельный тред который инициировал ошибку у неполучивших ответ вовремя и удалял их из постоянной структуры.

Absurd ★★★
()
Ответ на: комментарий от legolegs

>Есть непроложное правило: ни деструктор, ни функция swap не могут генерировать исключений (Герб Саттер). Иначе ломается C++

> Непреложные правила все записаны в стандарте языка и такого там нет.

Это неявно предполагается при обработке исключительных ситуаций в конструкторах при выделении как одиночных объектов, так и массивов. Смело можно считать непреложной истиной :)

А ты читал Саттера?

dave ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.