problem with weakref.proxy

W

Walter Haslbeck

Hello,

I'm a completly Python newbie. As a first learning project I want to port
an game-engine I have writte some time ago in pure C to Python using OO
methods.

So I created a base-class 'GOb' (GameObject). This class should have
a sorted list of all instances stored as as class-attribte __olist[].

When I add a reference of the new created object in the constructor
(with GOb.__olist.append(self)) I have 2 references to each created
instance and if I 'del' the instance the destructor is not called,
because __olist[] holds another reference to that object.

Now I got to tip to use the weakref module. Well I thought this should
be exactly what I need and changed the program to add not a reference
to the instances but a weakref.proxy.

So now my instances are deleted really deleted when I use 'del', but:
How do get corresponding weakref.proxy object out of __olist[]?

I tried to use the callback parameter from weakref.proxy, but at the
time when the callback takes place, the proxy object is allready 'dead',
I get an Exception when I try to remove the proxy-instance from __olist[]:

Exception exceptions.ReferenceError: 'weakly-referenced object no
longer exists' in <function proxy_callback at 0x4040c02c> ignored

And if I iterate throu all objects in __olist[] I get an
'ReferenceError: weakly-referenced object no longer exists'


please look at the following source:

################################################################################

import weakref

class GOb:

__olist=[]

def c_show_all():
print "all GObs, sorted by priority:"
for i in GOb.__olist:
i.show()

def proxy_callback(x):
print "weakref.proxy callback"
GOb.__olist.remove(x)

proxy_callback=staticmethod(proxy_callback)
c_show_all=staticmethod(c_show_all)

def __init__(self, name="GOB", priority=0):
self.priority=priority
self.name=name
ref=weakref.proxy(self, GOb.proxy_callback)
GOb.__olist.append(ref)
GOb.__olist.sort()

def __del__(self):
print "Destruktor called for GOB " + self.name

def show(self):
print self.name + " " + str(self.priority)

def __cmp__(self, other):
if self.priority < other.priority:
return -1
elif self.priority == other.priority:
return 0
else:
return 1

if __name__ == '__main__':

a=GOb("T1", 0)
b=GOb("T2", 2)
c=GOb("T3", 1)

GOb.c_show_all()
print "delete T1:"
del a
GOb.c_show_all()

################################################################################

any hints?

Walter
 
J

Jp Calderone

Hello,

I'm a completly Python newbie. As a first learning project I want to port
an game-engine I have writte some time ago in pure C to Python using OO
methods.

So I created a base-class 'GOb' (GameObject). This class should have
a sorted list of all instances stored as as class-attribte __olist[].

When I add a reference of the new created object in the constructor
(with GOb.__olist.append(self)) I have 2 references to each created
instance and if I 'del' the instance the destructor is not called,
because __olist[] holds another reference to that object.

Now I got to tip to use the weakref module. Well I thought this should
be exactly what I need and changed the program to add not a reference
to the instances but a weakref.proxy.

So now my instances are deleted really deleted when I use 'del', but:
How do get corresponding weakref.proxy object out of __olist[]?

I tried to use the callback parameter from weakref.proxy, but at the
time when the callback takes place, the proxy object is allready 'dead',
I get an Exception when I try to remove the proxy-instance from __olist[]:

A WeakKeyDictionary or WeakValueDictionary might be a better fit for your
problem. There is no need for you to remove the object from these manually.
When the object is no longer referenced anywhere else, it also no longer
belongs to the dictionary.
>>> class foo: pass ...
>>> import weakref
>>> d = weakref.WeakValueDictionary()
>>> x = foo()
>>> d['key'] = x
>>> print d.items()
[('key' said:
>>> del x
>>> print d.items() []
>>>

Jp
 
P

Peter Otten

Walter said:
Hello,

I'm a completly Python newbie. As a first learning project I want to port
an game-engine I have writte some time ago in pure C to Python using OO
methods.

So I created a base-class 'GOb' (GameObject). This class should have
a sorted list of all instances stored as as class-attribte __olist[].

When I add a reference of the new created object in the constructor
(with GOb.__olist.append(self)) I have 2 references to each created
instance and if I 'del' the instance the destructor is not called,
because __olist[] holds another reference to that object.

Now I got to tip to use the weakref module. Well I thought this should
be exactly what I need and changed the program to add not a reference
to the instances but a weakref.proxy.

So now my instances are deleted really deleted when I use 'del', but:
How do get corresponding weakref.proxy object out of __olist[]?

I tried to use the callback parameter from weakref.proxy, but at the
time when the callback takes place, the proxy object is allready 'dead',
I get an Exception when I try to remove the proxy-instance from __olist[]:

Exception exceptions.ReferenceError: 'weakly-referenced object no
longer exists' in <function proxy_callback at 0x4040c02c> ignored

And if I iterate throu all objects in __olist[] I get an
'ReferenceError: weakly-referenced object no longer exists'


please look at the following source:

[...]

I'd recommend being lazy and using a WeakValueDictionary instead of building
the machinery on your own. Your code would then look similar to the
following:

import weakref

class GOb:

all = weakref.WeakValueDictionary()

def c_show_all():
print "all GObs, sorted by priority:"
values = GOb.all.values()
values.sort()
for i in values:
print i

c_show_all = staticmethod(c_show_all)

def __init__(self, name="GOB", priority=0):
self.priority = priority
self.name = name
GOb.all[id(self)] = self

def __del__(self):
print "Destruktor called for GOB " + self.name

def __str__(self):
return self.name + " " + str(self.priority)

def __cmp__(self, other):
return cmp(self.priority, other.priority)

if __name__ == '__main__':

a=GOb("T1", 0)
b=GOb("T2", 2)
c=GOb("T3", 1)

GOb.c_show_all()
print "delete T1:"
del a
GOb.c_show_all()


Peter
 
P

Peter Otten

Peter said:
I'd recommend being lazy and using a WeakValueDictionary instead of
building the machinery on your own. Your code would then look similar to

If you want to keep your original design, just use ref instead of proxy:

import weakref

class GOb:

__olist=[]

def c_show_all():
print "all GObs, sorted by priority:"
for i in GOb.__olist:
i().show()

def proxy_callback(x):
print "weakref.proxy callback"
GOb.__olist.remove(x)

proxy_callback=staticmethod(proxy_callback)
c_show_all=staticmethod(c_show_all)

def __init__(self, name="GOB", priority=0):
self.priority=priority
self.name=name
ref=weakref.ref(self, GOb.proxy_callback)
GOb.__olist.append(ref)
GOb.__olist.sort(lambda x, y: cmp(x(), y()))

def __del__(self):
print "Destruktor called for GOB " + self.name

def show(self):
print self.name + " " + str(self.priority)

def __cmp__(self, other):
if self.priority < other.priority:
return -1
elif self.priority == other.priority:
return 0
else:
return 1

if __name__ == '__main__':

a=GOb("T1", 0)
b=GOb("T2", 2)
c=GOb("T3", 1)

GOb.c_show_all()
print "delete T1:"
del a
GOb.c_show_all()

The line that failed in your original version is

GOb.__olist.remove(x)

because x is found in __olist by comparing against the list items - and the
proxy automatically delegates this to the no longer existing object. With
ref you have to explicitly dereference by calling (as seen in
__olist.sort() in the __init__() method).
As that step is not performed by the list.remove() method, only ref instead
of GOb instances are compared and the callback succeeds.

Peter
 
W

Walter Haslbeck

Peter Otten said:
I'd recommend being lazy and using a WeakValueDictionary instead of building
the machinery on your own. Your code would then look similar to the
following:

thanks for the tip, but:

[...]
def c_show_all():
print "all GObs, sorted by priority:"
values = GOb.all.values()
values.sort()
[...]

I don't want the list to be sorted in the method c_show_all, but when a
new instance is created. Sort a list is an expensive operation, and
c_show_all will be called on every game frame, but new objects are
created much less.

Walter
 
W

Walter Haslbeck

Peter Otten said:
If you want to keep your original design, just use ref instead of proxy:

Thanks! Thats what I was looking for!

And: after I posted the original article I tried another method:

I didn't use weakref, but put a real reference of the instance
into __olist and checked in c_show_all with sys.getrefcount()
if the object is referenced by other names. If not, I removed
the object from __olist[].

Well, that worked. But I like the weakref.ref method much more!

Walter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top