What is the difference between `weakref.ref` and `weakref.proxy` in Python?
answer
- Neither keeps the object alive
- One is a callable, one impersonates
- The dead cases differ sharply
- None versus ReferenceError
- Proxies are never hashable
basics
~20 sBoth refer to an object without keeping it alive. A weakref.ref is a callable: call it to get the object, or None once it is gone. A weakref.proxy forwards attribute access through, and raises ReferenceError once it is gone.
solid answer
~50 sA **weak reference** points at an object without contributing to its reference count, so the object can be collected while the weak reference still exists. `weakref.ref(obj)` gives you a callable: `r()` returns the object, or `None` after it dies, which forces you to check before use and makes the dead case explicit. `weakref.proxy(obj)` gives you a stand-in that forwards attribute access, item access and most operators to the referent, so it reads like the object itself -- but any use after the referent dies raises `ReferenceError`, and proxies are deliberately unhashable, so they cannot be dict keys or set members. Use `ref` when you want to test liveness and branch; use `proxy` when you want a drop-in that must never silently outlive its target. Both accept an optional callback invoked when the referent is collected.
code
python · 19 linesimport weakref
class Node:
def __init__(self, label):
self.label = label
n = Node("root")
r = weakref.ref(n)
p = weakref.proxy(n)
print(r().label, p.label)
del n
print(r()) # None: the referent is gone
try:
p.label
except ReferenceError:
print("proxy raised ReferenceError")go deeper
Know that a weak reference lets you point at an object without keeping it alive, that weakref.ref(obj) must be called with r() to get the object back, and that it returns None once the object is gone.
Contrast the two dead-object behaviours precisely -- None from a called ref, ReferenceError from a proxy -- and be ready for the TypeError cases: list, dict, tuple, int, str and __slots__ classes without '__weakref__'.
Demonstrate the design use: weak back-pointers to break parent-child cycles so structures free promptly under reference counting, and callbacks to deregister entries. Explain when a proxy's loud failure beats a ref's silent None.
Argue about where weakness belongs in an architecture. Lifetime that is implicit in a reference graph is hard to reason about at scale, so treat weak references as a targeted tool for caches and back-pointers, not a general answer to memory growth.
### What a weak reference is CPython keeps an object alive as long as something refers to it. A **weak reference** deliberately opts out of that: it lets you name an object without owning it, so the object's lifetime is decided entirely by its other, strong references. The moment those go away, the object is collected and the weak reference goes dead. This is how you build caches, registries, back-pointers and observer lists that do not, by their mere existence, prevent anything from ever being freed. ### `weakref.ref` `r = weakref.ref(obj)` returns a weak reference **object**. It is not the referent and does not pretend to be. To reach the referent you call it: `r()` returns the object if it is still alive and `None` if it is not. The API is blunt on purpose -- the dead case is a value you must handle, which is exactly what you want when the whole point of the reference is that the object may vanish. A `weakref.ref` is hashable when its referent is (and keeps hashing consistently after the referent dies, provided it was hashed while alive), and two weak references compare equal if their live referents compare equal. That makes `ref` objects usable as dict keys, which is the machinery under `weakref.WeakKeyDictionary`. `weakref.ref` also takes an optional second argument, a callback receiving the weak reference itself, called when the referent is about to be finalized -- useful for removing the entry from a registry. ### `weakref.proxy` `p = weakref.proxy(obj)` returns a proxy that forwards operations to the referent. `p.attr`, `p[key]`, `p + other`, `len(p)` all reach through to the real object, so a proxy can be passed to code that has no idea weak references are involved. When the referent dies, every operation on the proxy raises `ReferenceError` instead of returning `None`, so a stale proxy fails loudly at the point of use rather than producing a confusing `AttributeError: 'NoneType' object has no attribute ...` somewhere else. Proxies have important limits. They are **not hashable**, regardless of the referent, because the referent's hash could become unavailable mid-life; that rules out using a proxy as a dict key or set member. `type(p)` is a proxy type, not the referent's class, and `isinstance` checks against the referent's class fail, so a proxy is not a perfect substitute. And a proxy of a proxy is not something to build on. ### What can be weakly referenced This is the part people get wrong in interviews. Not every object supports weak references: the type must have a slot for the list of weak references to it, exposed as `__weakref__`. Instances of classes defined in Python get one for free. Many built-in types do not: `list`, `dict`, `tuple`, `int`, `str` and bare `object()` instances all raise `TypeError: cannot create weak reference to ...`. `set` instances and functions, classes, methods and modules do support it. `list` and `dict` gain support if you subclass them, because your Python-level subclass adds the slot; `tuple` and `int` do not, even when subclassed. The other common cause of a surprising `TypeError` is `__slots__`. A class that defines `__slots__` and does not include `'__weakref__'` in it has no such slot, and its instances cannot be weakly referenced -- which in turn means they cannot be stored in a `weakref.WeakValueDictionary` or used with `weakref.finalize`. Adding `'__weakref__'` to the slots tuple restores the ability at the cost of one pointer per instance. ### Choosing between them, and what else lives here Use `weakref.ref` in code you own, where an explicit "is it still there?" check is honest and readable, and where you need hashability or a callback. Use `weakref.proxy` when you are handing the reference to code that expects the plain object and you want the dead case to be an immediate, obvious error. In practice much real code uses neither directly: `weakref.WeakValueDictionary`, `weakref.WeakKeyDictionary` and `weakref.WeakSet` wrap the mechanism into containers, and `weakref.finalize` wraps it into a cleanup registration. `weakref.getweakrefcount(obj)` tells you how many weak references currently point at an object, which is a handy debugging aid when a registry is not clearing itself. The canonical design use is breaking ownership cycles without relying on the cyclic collector: a parent holds strong references to its children, and each child holds a weak reference back to its parent. Dropping the parent then frees the whole structure promptly, and a child that outlives its parent discovers it cleanly rather than pinning a dead tree in memory.
- Why does `weakref.ref([])` raise `TypeError`?A type can be weakly referenced only if it has a slot for its weak-reference list, surfaced as `__weakref__`. Built-in `list` does not carry one, and neither do `dict`, `tuple`, `int`, `str` or bare `object()` instances. Subclassing `list` or `dict` in Python adds the slot, so a subclass instance works; subclassing `tuple` or `int` does not help.
- How do you use weak references to break a parent-child ownership cycle?Give the parent strong references to its children and give each child a weak reference back to the parent. No cycle exists, so dropping the parent frees the whole structure by reference counting alone, without waiting for the cyclic collector. Children reach the parent by calling the weak reference and handle the `None` case for an orphaned child.
- What does the optional callback argument to `weakref.ref` do?`weakref.ref(obj, callback)` calls `callback(weakref_object)` when the referent is about to be finalized, passing the weak reference rather than the dead object. It is the hook used to deregister entries from a registry or index. The callback must not assume the referent is reachable, and an exception inside it is unraisable, exactly as in a finalizer.
A ref is a phone number you have to dial and may find disconnected; a proxy is a receptionist who patches you straight through, or tells you flatly that the person no longer works here.
saying these in an interview costs you the question
- Says a weak reference just decrements the refcount
- Thinks `weakref.ref(obj)` can be used like the object
- Expects a dead proxy to return `None`
- Assumes any object can be weakly referenced
- Uses a `weakref.proxy` as a dictionary key
- Confuses a weak reference with a shallow copy