AI Skill Report Card

Using Weakref

A-87·Aug 22, 2026·Source: Web
14 / 15
Python
import weakref class Node: pass obj = Node() r = weakref.ref(obj) # weak reference, doesn't keep obj alive print(r()) # -> <Node object> (call it to get the referent) del obj print(r()) # -> None, object was garbage collected
Recommendation
Add a brief example showing WeakKeyDictionary usage explicitly since it's referenced in the decision table but not demonstrated
13 / 15
  1. Identify the memory issue: reference cycles, caches growing unbounded, or child objects keeping parents alive.
  2. Choose the right weakref tool (see decision table below).
  3. Use .callback for cleanup notification if you need to know when the referent dies.
  4. Test garbage collection behavior explicitly (del obj; gc.collect()) to confirm no leaks.

Progress:

  • Identify objects that should NOT be kept alive by references
  • Pick appropriate weakref primitive (ref, proxy, WeakValueDictionary, WeakKeyDictionary, WeakSet)
  • Handle the case where the referent has been collected (check for None or ReferenceError)
  • Add callbacks if cleanup/logging is needed
  • Verify with gc module or memory profiling
Recommendation
Show a bad/incorrect example (e.g., using __del__ improperly) alongside the good pattern to reinforce contrast
NeedUse
Single weak reference, explicit call to dereferenceweakref.ref(obj, callback=None)
Weak reference that behaves like the object itselfweakref.proxy(obj, callback=None)
Dict mapping keys -> weakly-referenced values (cache pattern)weakref.WeakValueDictionary()
Dict mapping weakly-referenced keys -> values (annotate objects w/o owning them)weakref.WeakKeyDictionary()
Set of objects without keeping them aliveweakref.WeakSet()
Weak reference to a bound method (doesn't keep instance alive)weakref.WeakMethod()
Finalization/cleanup callback tied to object lifetime, safer than __del__weakref.finalize(obj, func, *args, **kwargs)
18 / 20

Example 1: Caching expensive objects without leaking memory

Input: Cache computed objects by key, but let them be garbage collected when nothing else references them.

Output:

Python
import weakref class ExpensiveObject: def __init__(self, value): self.value = value cache = weakref.WeakValueDictionary() def get_object(key): obj = cache.get(key) if obj is None: obj = ExpensiveObject(key) cache[key] = obj return obj a = get_object("x") print(len(cache)) # 1 del a import gc; gc.collect() print(len(cache)) # 0, auto-removed

Example 2: Observer pattern without preventing listener cleanup

Input: A subject notifies observers, but shouldn't keep dead observers alive.

Output:

Python
import weakref class Subject: def __init__(self): self._observers = weakref.WeakSet() def register(self, observer): self._observers.add(observer) def notify(self, *args): for obs in self._observers: obs.update(*args)

Example 3: Using finalize for cleanup instead of __del__

Input: Run cleanup code when an object is garbage collected, safely (no __del__ pitfalls).

Output:

Python
import weakref class Resource: def __init__(self, name): self.name = name self._finalizer = weakref.finalize(self, print, f"Cleaning up {name}") def close(self): self._finalizer() r = Resource("db-connection") del r # prints "Cleaning up db-connection"

Example 4: Weak reference to a bound method (avoid keeping instance alive via callback)

Input: Register a callback that is a bound method without preventing the instance from being collected.

Output:

Python
import weakref class Handler: def on_event(self): print("event handled") h = Handler() weak_method = weakref.WeakMethod(h.on_event) callback = weak_method() if callback is not None: callback() del h print(weak_method()) # None
Recommendation
Include a note on thread-safety considerations when using weakref collections in concurrent contexts
  • Not all objects support weak references: built-ins like int, list, dict, tuple (without __slots__ workaround) can't be weakly referenced directly. Custom classes support it by default unless __slots__ excludes __weakref__.
  • Always check if ref() or proxy() returned None/raised ReferenceError before using the result — the referent may already be gone.
  • Prefer weakref.finalize over __del__ for cleanup logic — it's more predictable, supports multiple finalizers, and doesn't resurrect objects.
  • Use WeakValueDictionary/WeakKeyDictionary for caches and registries where entries should disappear automatically once no other strong references exist.
  • Use proxy() when you want transparent attribute access instead of calling ref()().
  • Keep callbacks passed to ref()/proxy() simple; they run after the object is already gone (during GC), so the callback receives the dead weakref, not the object.
  • Storing the weakref in a way that outlives its usefulness: iterating over a WeakValueDictionary/WeakSet while objects are being collected can raise RuntimeError: dictionary changed size during iteration. Take a snapshot (list(...)) before iterating if mutation during iteration is possible.
  • Assuming a weakref keeps working forever: always re-check dereferenced weakrefs before use, especially across function boundaries or after await/context switches.
  • Trying to weakly reference an object without __weakref__ slot: define __slots__ = ('__weakref__', ...) explicitly if using __slots__ and weakref support is needed.
  • Using __del__ for cleanup instead of finalize: __del__ can resurrect objects, interacts badly with reference cycles and exceptions during interpreter shutdown.
  • Forgetting that a callback fires with the dead reference, not the object: don't expect obj state inside the callback; the object's state is already unreachable.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
13/15
Examples
18/20
Completeness
18/20
Format
14/15
Conciseness
14/15