6 def __init__(self
, do_delete
):
9 self
.__do
_delete
= do_delete
13 if v_id
in self
.__refs
:
16 v
= weakref
.ref(v
, callback
=self
.__do
_delete
)
22 self
.__keys
.append(id(k
))
23 self
.__keys
.append(id(v
))
28 return tuple(self
.__keys
), tuple(self
.__refs
.values())
31 def deduped(*, global_keys
=()):
32 """decorator that causes functions to deduplicate their results based on
33 their input args and the requested globals. For each set of arguments, it
34 will always return the exact same object, by storing it internally.
35 Arguments are compared by their identity, so they don't need to be
40 # for functions that don't depend on global variables
42 def my_fn1(a, b, *, c=1):
47 # for functions that depend on global variables
48 @deduped(global_keys=[lambda: my_global])
49 def my_fn2(a, b, *, c=2):
50 return a + b * c + my_global
53 global_keys
= tuple(global_keys
)
54 assert all(map(callable, global_keys
))
57 if isinstance(f
, (staticmethod, classmethod)):
58 raise TypeError("@staticmethod or @classmethod should be applied "
59 "to the result of @deduped, not the other way"
66 def wrapper(*args
, **kwargs
):
67 key_builder
= _KeyBuilder(lambda _
: map.pop(key
, None))
69 key_builder
.add(None, arg
)
70 for k
, v
in kwargs
.items():
72 for global_key
in global_keys
:
73 key_builder
.add(None, global_key())
74 key
, refs
= key_builder
.finish()
77 retval
= f(*args
, **kwargs
)
78 # keep reference to stuff used for key to avoid ids
79 # getting reused for something else.
80 map[key
] = retval
, refs