weakrefs and things that point to each other

Duncan Booth duncan at NOSPAMrcp.co.uk
Fri Jun 22 05:37:56 EDT 2001


Jonathan Gardner <gardner at cardomain.com> wrote in
news:9gu4eg$838$1 at brokaw.wa.com: 

> Okay, here''s the idea. Object A can have many things of Class B.
> Object B can be had by many things of Class A. When A is destroyed, it
> wants to let B know that it no longer has it. When B is destroyed, it
> wants to let A know it no longer should have it.
> 
> But, because of circular references, we need to use weak refs on at
> least half of the equations. However, I want the B to destroy itself
> even when there is one or more A's pointing to it. So I will implement
> weakrefs on both sides.
If you have weak references on both sides then are you sure something is 
going to keep all those bees alive?

> Question 1: Is this the way it should be done?
Have you looked at the WeakValueDictionary class? If all you want to do is 
keep a collection of weakly referenced objects it will handle the deletion 
automatically (use the id as a convenient key). If you need to do more on 
the deletion use the callback option on a weak reference instead of doing 
things in __del__.

> 
> Question 2: "if b in self.has:" will not ever return true because:
>>>>  a = Something()
>>>> b = a
>>>> c = weakref.ref(a) b is a 1 c is a 0 c() is a 1
you have to remember to dereference the weak references before comparing. 
Something like WeakValueDictionary makes this transparent.

Here is how I would do it:
---- begin ----
from weakref import WeakValueDictionary

class A:
    def __init__(self, name=""):
        self.has = WeakValueDictionary()
        self.name = name
    def __repr__(self):
        return "<A %s at %s>" % (self.name, id(self))

    def add(self, b):
        self.has[id(b)] = b
        b.addedby(self)

class B:
    def __init__(self, name=""):
        self.hadby = WeakValueDictionary()
        self.name = name
    def __repr__(self):
        return "<B %s at %s>" % (self.name, id(self))

    def addedby(self, a):
        self.hadby[id(a)] = a

b1, b2, b3 = B('b1'), B('b2'), B('b3')
a1, a2, a3 = A('a1'), A('a2'), A('a3')
a1.add(b1)
a1.add(b2)
a2.add(b2)
a2.add(b3)
a3.add(b3)
a3.add(b1)
print a1, "has", a1.has.values()
print a2, "has", a2.has.values()
print a3, "has", a3.has.values()
print b1, "had by", b1.hadby.values()
print b2, "had by", b2.hadby.values()
print b3, "had by", b3.hadby.values()
del a2
print "Deleted a2:"
print a1, "has", a1.has.values()
print a3, "has", a3.has.values()
print b1, "had by", b1.hadby.values()
print b2, "had by", b2.hadby.values()
print b3, "had by", b3.hadby.values()

del b2
print "Deleted b2:"
print a1, "has", a1.has.values()
print a3, "has", a3.has.values()
print b1, "had by", b1.hadby.values()
print b3, "had by", b3.hadby.values()
--- end ---
Which produces output like this:
<A a1 at 8335484> has [<B b2 at 8353644>, <B b1 at 8134148>]
<A a2 at 8328116> has [<B b3 at 8340012>, <B b2 at 8353644>]
<A a3 at 8341268> has [<B b1 at 8134148>, <B b3 at 8340012>]
<B b1 at 8134148> had by [<A a3 at 8341268>, <A a1 at 8335484>]
<B b2 at 8353644> had by [<A a2 at 8328116>, <A a1 at 8335484>]
<B b3 at 8340012> had by [<A a3 at 8341268>, <A a2 at 8328116>]
Deleted a2:
<A a1 at 8335484> has [<B b2 at 8353644>, <B b1 at 8134148>]
<A a3 at 8341268> has [<B b1 at 8134148>, <B b3 at 8340012>]
<B b1 at 8134148> had by [<A a3 at 8341268>, <A a1 at 8335484>]
<B b2 at 8353644> had by [<A a1 at 8335484>]
<B b3 at 8340012> had by [<A a3 at 8341268>]
Deleted b2:
<A a1 at 8335484> has [<B b1 at 8134148>]
<A a3 at 8341268> has [<B b1 at 8134148>, <B b3 at 8340012>]
<B b1 at 8134148> had by [<A a3 at 8341268>, <A a1 at 8335484>]
<B b3 at 8340012> had by [<A a3 at 8341268>]
Found b3 in a3




-- 
Duncan Booth                                             duncan at rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?



More information about the Python-list mailing list