NarūnasK
NarūnasK

Reputation: 4950

Python delete value by destroying reference to it

I have a complex data structure (multidimensional dict with lists of tuples) which is filtered by multiple functions and finally returns a list or dictionary of deletable objects, something similar to this:

import sys

d1 = {'a': 1, 'd': 3, 'f': 4, 'g': 5, 'h': 6, 'j': 7, 'k': 8, 'l': 9, 's': 2}
d2 = {1: 'a', 2: 's', 3: 'd', 4: 'f', 5: 'g', 6: 'h', 7: 'j', 8: 'k', 9: 'l'}

def filter1(d1, d2):
    dd1 = {k:v for k,v in d1.items() if k not in ['a','l']}
    dd2 = {k:v for k,v in d2.items() if k not in [4, 6]}
    return dict(dd1.items() + dd2.items())

def filter2(d):
    return {k:v for k,v in d.items() if not isinstance(v, int) if 3 < k < 8}

f = filter2(filter1(d1,d2))

By looking at the reference count it seems that these objects point to the same values. Further their ids are exactly the same.

In [2]: sys.getrefcount(f[5])
Out[2]: 15

In [3]: sys.getrefcount(d2[5])
Out[3]: 15

In [4]: 

In [4]: test = d2[5]

In [5]: 

In [5]: sys.getrefcount(f[5])
Out[5]: 16

In [6]: sys.getrefcount(d2[5])
Out[6]: 16

In [7]: 

In [7]: id(f[5])
Out[7]: 140313621257992

In [8]: id(d2[5])
Out[8]: 140313621257992

After filtering in the final product I essentially get a dict of references. Deleting objects from that dict should automagically remove original attributes from either d1, d2.

I wonder if it is possible to delete these objects by id, or somehow destroy references to their values and let garbage collector do the job?

Otherwise my filter functions must keep track of which dict[key] and from which list[index] each object has been taken and then delete explicitly. This is quite difficult and error prone job...

Desired output:

for v in f.values():
    del v

d2 = {1: 'a', 2: 's', 3: 'd', 4: 'f', 6: 'h', 8: 'k', 9: 'l'}

Upvotes: 0

Views: 350

Answers (2)

Serge Ballesta
Serge Ballesta

Reputation: 148890

I could not directly adapt your example with it, but IMHO the closest Python tool to your requirement is weak references.

A weak reference can reference an object as long as it exists, but when the last hard reference to it disappears, the object is no longer valid and can be garbage collected. When you try to get a reference to a no longer alive object, you just get None.

So if you want that removing objects in a reference dict removes it from the original dict, you have to replace the object in the original dict with a weak reference to the object in the ref dict. That way, when you remove the object from the other dict, the weak ref is not enough to keep the object alive and it disappears.

But as I said in the beginning, I could not adapt this to your example, because you construct you filtered lists by keeping good values, when the weakref would require you to first put everythin in filtered list and then delete what you do not want to keep.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1121554

No, you cannot use the id() of an object to delete references to the object. You'll have to delete the correct keys from d1 and d2 to remove those references.

Have your filter return a set of keys that need deleting from those dictionaries, then loop over the set and explicitly remove those:

for key in filter2(filter1(d1,d2)):
    d1.pop(key, None)
    d2.pop(key, None)

I used dict.pop() to allow for the key to be missing.

Upvotes: 3

Related Questions