2

Piggybacking on this question, say i have a container for a weakreference:

import weakref

class Foo(object):
    a = lambda *_: None

    def __init__(self, a):
        self.a = weakref.ref(a, self._remove)

    def _remove(self, *args):
        self.__del__(self)


class Bar(object):
    pass


>>> bar = Bar()
>>> foo = Foo(bar)
>>> del bar
>>> foo
<__main__.Foo object at 0x...>

I thought of storing the Foo instance in a static WeakKeyDictionary container, with the a attribute as a key, and using weakref.proxy of the instance everywhere--but that seems...inefficient. What's the best way to make it so that the Foo instance deletes itself when its reference to a dies?

Community
  • 1
  • 1
Noob Saibot
  • 4,573
  • 10
  • 36
  • 60
  • 1
    Can you give a use case for this? – icktoofay Jun 07 '14 at 03:14
  • @icktoofay: This is a container to both cache objects and prevent circular referencing. Theoretically, the container itself could be cached in a list, or something. If the references inside the container are no longer applicable, there's no reason for the container to stay in memory. – Noob Saibot Jun 07 '14 at 03:16
  • What do you expect the last line (`foo`) to output? A `NameError`? `None`? `` or something similar? – icktoofay Jun 07 '14 at 03:17
  • @icktoofay: `NameError`. As if i deleted the instance myself. – Noob Saibot Jun 07 '14 at 03:18
  • What if I were to, say, store `foo` in a list, maybe along with some other items, like `items = [1, 2, foo, 3]`. Would you expect it to call `__delitem__` on the list to end up with something like `[1, 2, 3]`? – icktoofay Jun 07 '14 at 03:22

1 Answers1

2

You can't. I just spent some time digging through the Python source and ctypes documentation to ironically show how one might really delete (aka Py_DECREF until deallocated) an object until I gave up. The point is, you don't really want to do this. Python manages its own memory for a reason. Sure, it gives you access to things like weak references, but in no case will Python break a strong reference.

What you are proposing is to have an object reach into the environments of every bit of code loaded into the Python interpreter to rip out any references to itself. weakref has to rip out references too, but it only has to remove the references from the weakref object; it doesn't have to touch the object holding a reference to the weakref. To remove a reference in the way you propose would be at least invasive and most likely impossible.

To see why it would be impossible, consider how one might write a Python module in C that defines a type. Each instance of the object is going to hold some PyObject pointers to things it cares about. Some of these might be exposed to Python through properties, while others might remain internal. Suppose one of these internal references referenced one of your Foo objects. For it to 'delete' itself, it would have to reach into our C type and NULL out the reference. But to Python code, the C struct defining the object is opaque. If you dug into it with ctypes, you could inspect the bytes, but who's to know whether some sequence of bytes is a pointer to your object or an int that just happens to have the same value as the address of your object? You can't, at least without knowing implementation details of that type. And you can't handle every case, because someone can add another case just by importing another module written in C. You can't anticipate everything.

So what can you do? If you're deadset on doing something like this, you can mimic weakref's interface. Basically, make a new class that holds a reference to your class; to avoid ambiguity, I'll call this a fakeref. When it's called, it returns the instance of your class. Your class holds weak references1 to all of its fakerefs. Whenever your Foo class wants to delete itself, loop over the fakerefs, Noneing out the references to the Foo. Voilà; your class can 'delete' itself as desired and all of the fakerefs will now return None. But just as with weakrefs, storing the result of a call will make it a strong reference again, and your class will not be able to delete itself in the manner you desire.

All this said, I don't think you've presented a good enough case for why this is necessary. All you've said is that "there's no reason for it to stay in memory". Well, there is: it needs to be there for the objects that reference it. If, at some point in time, it becomes useless, then your objects shouldn't be holding a reference to it. When the objects referencing it don't care about it any more, they should remove those references. Then Python will clean it up with no further intervention on your part.


1 If you don't want to rely on weak references, your fakeref can implement __del__ and remove itself from your Foo instance it holds a reference to (if not None).

icktoofay
  • 126,289
  • 21
  • 250
  • 231