By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
445,857 Members | 1,793 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 445,857 IT Pros & Developers. It's quick & easy.

Generator inside a class prevent __del__ ??

P: n/a
Hi,

I run across this problem, and couldn't find any solution (python 2.2.2)
:

Code :
===========
from __future__ import generators
class titi: def __init__(self):
print "init"
def __del__(self):
print "del"
def Gen(self):
yield 1
c = titi() init c = [] del
==============
Here, everything is normal...
But creating a generator :

Code :
===========
class toto: def __init__(self):
print "init"
self.Coroutine = self.Gen()
def __del__(self):
print "del"
def Gen(self):
yield 1
a = toto() init c = []

<--- Nothing there !!!
==============

I can't understand why the destructor is not called when a generator is
created, and what I should do to have a "correct" behavior.
(perhaps I missed something obvious, but I can't find it )
Thank you for any help,

Emmanuel


Jul 18 '05 #1
Share this Question
Share on Google+
13 Replies


P: n/a

"Emmanuel" <ea*****@free.fr> wrote in message
news:40***************@free.fr...
I run across this problem, and couldn't find any solution (python 2.2.2) .... Here, everything is normal...
But creating a generator :
You both defined generator function and called it to create generator
iterator.

Code :
===========
class toto: def __init__(self):
print "init"
self.Coroutine = self.Gen()
This creates a reference loop. Delete this (and correct typo below) and
'problem' will disappear.
def __del__(self):
print "del"
def Gen(self):
If you do not really use self in the resulting iterator, define this
outside of the class without self as a parameter, and problem will
disappear.
yield 1
a = toto()

did you mean 'c = toto()'?
init c = []

<--- Nothing there !!!
==============

I can't understand why the destructor is not called when a generator is
created, and what I should do to have a "correct" behavior.


Either do not create reference loop or break it with del c.Coroutine.

Terry J. Reedy

Jul 18 '05 #2

P: n/a
In article <40***************@free.fr>, Emmanuel <ea*****@free.fr>
wrote:
class toto: def __init__(self):
print "init"
self.Coroutine = self.Gen()
def __del__(self):
print "del"
def Gen(self):
yield 1
a = toto() init c = [] <--- Nothing there !!!


First of all, "a" is still referencing your toto object. I think you
meant "a = []" here. But even if you did "a = []", the destructor
still isn't called. There must still be a reference to the object. My
guess is that the generator (directly or indirectly) is referencing the
object, creating a self referential loop.

Consider the following modification that merely references a function,
and does not create a generator:
class tata: .... def __init__(self):
.... print "init"
.... self.Coroutine = self.Gen
.... def __del__(self):
.... print "del"
.... def Gen(self):
.... pass
.... a=tata() init a=[]

Here's how to break that loop:
b=tata() init b.Coroutine=None
b=[] del


-Mark
Jul 18 '05 #3

P: n/a
Mark Day wrote:
still isn't called. There must still be a reference to the object. My
guess is that the generator (directly or indirectly) is referencing the
object, creating a self referential loop.


Python has a garbage collector that will try to find these objects with
cyclic references.

from test import *
a = toto() init a = None
import gc
gc.garbage [] gc.collect() 4 gc.garbage [<test.toto instance at 0x81cb78c>]


I checked out the documentation for that gc.garbage list and it says
that the collector can't free objects in cycles if the cyles have
objects that have __del__ methods. So it puts them in this list.

I wonder what other garbage collectors do in this situation? Anyone
know? Java?

Rob

Jul 18 '05 #4

P: n/a
Rob Nikander <rn*************@adelphia.net> wrote in
news:kN********************@adelphia.com:
I checked out the documentation for that gc.garbage list and it says
that the collector can't free objects in cycles if the cyles have
objects that have __del__ methods. So it puts them in this list.

I wonder what other garbage collectors do in this situation? Anyone
know? Java?


Most garbage collectors will do peculiar things if you have destructors or
finalizers in the objects. The problem is that if two objects with
finalizers reference each other there is no correct order to release the
objects that will guarantee that the other object still exists, so the
system either has to choose an arbitrary order, or refuse to call the
finalizers.

The .Net garbage collector is typical. Objects may have finalizers, and
these finalizers are called as part of the garbage collection. The system
guarantees that any finalizer is called exactly 0 or more times --- usually
it is called once when the object is garbage collected, but if the object
is never collected it may not be called at all, and if the object
resurrects itself (e.g. during the finalizer it assigns itself to a global
variable) the finalizer could be called more than once.

A separate thread pool is used for finalizers, so your finalizer could be
called while a user thread is executing a method on the object, and two
objects which refer to each other could have their finalizers called in any
order, or even simultaneously on separate threads. Effectively, this makes
finalizers useless in all but the most obscure situations.

When resources need to be released you should try to do it explicitly. In
..Net this is handled by the Dispose() method, and the finalizer can then
either try calling Dispose() if it has not yet been called, or could try
logging an error although even that may be problematic from a finalizer.
Jul 18 '05 #5

P: n/a


Terry Reedy a écrit :
"Emmanuel" <ea*****@free.fr> wrote in message
news:40***************@free.fr...
I run across this problem, and couldn't find any solution (python 2.2.2) ...
Here, everything is normal...
But creating a generator :


You both defined generator function and called it to create generator
iterator.


Yes, I don't have all the generators vocabulary yet...


Code :
===========
>> class toto: def __init__(self):
print "init"
self.Coroutine = self.Gen()


This creates a reference loop. Delete this (and correct typo below) and
'problem' will disappear.

def __del__(self):
print "del"
def Gen(self):
If you do not really use self in the resulting iterator, define this
outside of the class without self as a parameter, and problem will
disappear.


I didn't use self in order to provide a simple example. In my real class, self
is used...

yield 1
>> a = toto()

did you mean 'c = toto()'?


Yes, sorry for that...

init
>> c = []

<--- Nothing there !!!
==============

I can't understand why the destructor is not called when a generator is
created, and what I should do to have a "correct" behavior.


Either do not create reference loop or break it with del c.Coroutine.

Terry J. Reedy


Thank you very much for your answer, but I'm still not sure I understand it.
If I understand your words right, creating self.Coroutine as an iterator on
the generator function will create a reference on self, so if I want to use a
generator in a class ( and I really want to ), I must delete explicitly the
iterator before I destroy the object.

Trouble is, I _would_ like not to care about the lifetime of the object, and I
don't know where it will be destroyed.
Should I encapsulate this object in another one, like this :

import toto

class TotoCapsule:
def __init__( self ):
self.toto = toto.toto()
def __del__(self):
del self.toto.Coroutine
self.toto = None

And use TotoCapsule ?
But it means I have to write a lot of more code to access toto's method.
Is there a pattern I missed to dea l with that ?

Thanks a lot,

Emmanuel

Jul 18 '05 #6

P: n/a

"Emmanuel" <ea*****@free.fr> wrote in message
news:40***************@free.fr...


Terry Reedy a écrit :
>>> class toto:
def __init__(self):
print "init"
self.Coroutine = self.Gen()


This creates a reference loop. Delete this (and correct typo below) and 'problem' will disappear.


To amplify: the usual idiom for an instance-associated generator is to name
the generator function (method) __iter__ (with one param, self) and to
create and get a reference to the generator via iter() or let the for loop
mechanism do so for you.

c = C(*args)
cgen =iter(c)

Then there is no reference loop. And you can pass around the cgen object
just like any other. If you only need the instance after initialization to
get the generator and you only need one generator for the instance, then
combine the two lines into

cgen = iter(C(*args))

and the *only* reference to the instance is the one in the generator, which
will disappear at the end of a for loop or with an explicit 'del cgen'.

There is also the question whether you actually *need* to get rid of the
object while the program is still running instead of just letting the
program finish and clean up.

Terry J. Reedy

Jul 18 '05 #7

P: n/a
On Wed, Apr 21, 2004 at 02:53:33PM +0200, Emmanuel wrote:

Trouble is, I _would_ like not to care about the lifetime of the object, and I
don't know where it will be destroyed.


Then don't use __del__. Python can and will automatically collect cycles
when the objects *don't* define __del__ methods.

Out of curiousity, why are you defining __del__ anyway?

-Andrew.
Jul 18 '05 #8

P: n/a


Andrew Bennetts a écrit :
On Wed, Apr 21, 2004 at 02:53:33PM +0200, Emmanuel wrote:

Trouble is, I _would_ like not to care about the lifetime of the object, and I
don't know where it will be destroyed.


Then don't use __del__. Python can and will automatically collect cycles
when the objects *don't* define __del__ methods.

Out of curiousity, why are you defining __del__ anyway?

-Andrew.


I don't want to use __del__, but I suspected I had an issue with the destruction of
my objects, and used a log in __del__ to monitor the destruction.

But defining __del__ has also a lot of valuable utilisation, or so I think...

Emmanuel
Jul 18 '05 #9

P: n/a
In article <40***************@free.fr>, Emmanuel wrote:
Thank you very much for your answer, but I'm still not sure I understand it.
If I understand your words right, creating self.Coroutine as an iterator on
the generator function will create a reference on self, so if I want to use a
generator in a class ( and I really want to ), I must delete explicitly the
iterator before I destroy the object.

Trouble is, I _would_ like not to care about the lifetime of the object, and I
don't know where it will be destroyed.


Try looking up "weakref". (I've never used them myself, so I don't know
the exact syntax.)

Joe
Jul 18 '05 #10

P: n/a
On Thu, Apr 22, 2004 at 01:26:18AM +0200, Emmanuel wrote:


Andrew Bennetts a écrit :
On Wed, Apr 21, 2004 at 02:53:33PM +0200, Emmanuel wrote:

Trouble is, I _would_ like not to care about the lifetime of the object, and I
don't know where it will be destroyed.
Then don't use __del__. Python can and will automatically collect cycles
when the objects *don't* define __del__ methods.

Out of curiousity, why are you defining __del__ anyway?

-Andrew.


I don't want to use __del__, but I suspected I had an issue with the destruction of
my objects, and used a log in __del__ to monitor the destruction.


Except that __del__ affects how they are destructed :)

Weakrefs are probably a better choice for this, as they don't interfere with
the lifecycle of the object you're interested in, unlike __del__.
But defining __del__ has also a lot of valuable utilisation, or so I think...


It's only very very rarely useful, in my experience. Again, weakrefs are
probably more useful for what you have in mind.

-Andrew.
Jul 18 '05 #11

P: n/a


Andrew Bennetts a écrit :
On Thu, Apr 22, 2004 at 01:26:18AM +0200, Emmanuel wrote:


Andrew Bennetts a écrit :
On Wed, Apr 21, 2004 at 02:53:33PM +0200, Emmanuel wrote:
>
> Trouble is, I _would_ like not to care about the lifetime of the object, and I
> don't know where it will be destroyed.

Then don't use __del__. Python can and will automatically collect cycles
when the objects *don't* define __del__ methods.

Out of curiousity, why are you defining __del__ anyway?

-Andrew.


I don't want to use __del__, but I suspected I had an issue with the destruction of
my objects, and used a log in __del__ to monitor the destruction.


Except that __del__ affects how they are destructed :)

Weakrefs are probably a better choice for this, as they don't interfere with
the lifecycle of the object you're interested in, unlike __del__.
But defining __del__ has also a lot of valuable utilisation, or so I think...


It's only very very rarely useful, in my experience. Again, weakrefs are
probably more useful for what you have in mind.

-Andrew.


Ok, I think I don't understand anything anymore...

I thought __del__ was the destructor of the object, like the object::~object in C++ ( my
experience in programming is mainly from C++ ), and so __del__ shouldn't affect when they
are destructed.
And I thought weakref is a way to control the lifetime, ie when the ref count is
decremented, and when to call __del__.

From what you ( and others ) are saying, I'm proven wrong...

Do you know where I can find more information, beside python doc ?

Thanks,

Emmanuel
Jul 18 '05 #12

P: n/a


Terry Reedy a écrit :
"Emmanuel" <ea*****@free.fr> wrote in message
news:40***************@free.fr...


Terry Reedy a écrit :
> >>> class toto:
> def __init__(self):
> print "init"
> self.Coroutine = self.Gen()

This creates a reference loop. Delete this (and correct typo below) and 'problem' will disappear.

To amplify: the usual idiom for an instance-associated generator is to name
the generator function (method) __iter__ (with one param, self) and to
create and get a reference to the generator via iter() or let the for loop
mechanism do so for you.

c = C(*args)
cgen =iter(c)

Then there is no reference loop. And you can pass around the cgen object
just like any other. If you only need the instance after initialization to
get the generator and you only need one generator for the instance, then
combine the two lines into

cgen = iter(C(*args))

and the *only* reference to the instance is the one in the generator, which
will disappear at the end of a for loop or with an explicit 'del cgen'.


But I obviously need other references to my object in my code, my object isn't
modified by the generator only.
I want to resume my generator from time to time during the execution of my app,
and to modify the members of the objects somewhere else ( interaction between
my objects ).
Doing this result in my nicer programmation style than without generators.



There is also the question whether you actually *need* to get rid of the
object while the program is still running instead of just letting the
program finish and clean up.


I have a _lot_ of objects created whenever they want, and I don't know where
they will finish their job.
Additionnaly, I'm not developping only on PC, but also on platforms where there
is not so much memory avalaible.

By the way, it seems I still have a lot to understand on this subject.
Do you know any link, example, or whatever, that I could have a look at ?

Thank you very much for your answers,

Emmanuel
Jul 18 '05 #13

P: n/a
On Thu, Apr 22, 2004 at 11:12:27AM +0200, Emmanuel wrote:

Ok, I think I don't understand anything anymore...

I thought __del__ was the destructor of the object, like the object::~object in C++ ( my
experience in programming is mainly from C++ ), and so __del__ shouldn't affect when they
are destructed.
__del__ unfortunately *does* impact the lifetime of the object, at least in
CPython:
http://docs.python.org/lib/module-gc.html#l2h-403
http://docs.python.org/ref/customization.html#l2h-175

It's main use used to be to break reference cycles, because before Python
2.0 (or perhaps 1.6?), it couldn't automatically collect reference cycles
because it used a purely ref-count based approach. Now that cycles are
automatically collected, there's not much point in defining __del__ (and it
can actually have unexpected results).
And I thought weakref is a way to control the lifetime, ie when the ref count is
decremented, and when to call __del__.


No -- weakref doesn't affect the lifetime, that's it's point. It's a way to
have a reference to an object that doesn't keep the object alive if nothing
else is. As the documentation at
http://docs.python.org/lib/module-weakref.html says:

A weak reference to an object is not enough to keep the object alive:
when the only remaining references to a referent are weak references,
garbage collection is free to destroy the referent and reuse its memory
for something else.
From what you ( and others ) are saying, I'm proven wrong...


Do you know where I can find more information, beside python doc ?


Try googling for tutorials and things, there's probably stuff out there.
The Python docs are pretty good, though... the weakref module has pretty
comprehensive documentation, and the description of __del__ in the language
reference has big note that mentions that garbage-collection of cycles
doesn't work when __del__ methods are involved.

I've also found books such as Python in a Nutshell and the Python Essential
Reference to be quite good at pointing this sort of thing out, when I've
looked. I usually rely on the official Python docs, though.

-Andrew.
Jul 18 '05 #14

This discussion thread is closed

Replies have been disabled for this discussion.