2 - the popular ``_memoize_default`` works like a typical memoize and returns the
4 - ``CachedMetaClass`` uses ``_memoize_default`` to do the same with classes.
6 from functools
import wraps
10 _NO_DEFAULT
= object()
11 _RECURSION_SENTINEL
= object()
14 def _memoize_default(default
=_NO_DEFAULT
, inference_state_is_first_arg
=False,
15 second_arg_is_inference_state
=False):
16 """ This is a typical memoization decorator, BUT there is one difference:
17 To prevent recursion it sets defaults.
19 Preventing recursion is in this case the much bigger use than speed. I
20 don't think, that there is a big speed difference, but there are many cases
21 where recursion could happen (think about a = b; b = a).
24 def wrapper(obj
, *args
, **kwargs
):
25 # TODO These checks are kind of ugly and slow.
26 if inference_state_is_first_arg
:
27 cache
= obj
.memoize_cache
28 elif second_arg_is_inference_state
:
29 cache
= args
[0].memoize_cache
# needed for meta classes
31 cache
= obj
.inference_state
.memoize_cache
34 memo
= cache
[function
]
36 cache
[function
] = memo
= {}
38 key
= (obj
, args
, frozenset(kwargs
.items()))
42 if default
is not _NO_DEFAULT
:
44 rv
= function(obj
, *args
, **kwargs
)
52 def inference_state_function_cache(default
=_NO_DEFAULT
):
54 return _memoize_default(default
=default
, inference_state_is_first_arg
=True)(func
)
59 def inference_state_method_cache(default
=_NO_DEFAULT
):
61 return _memoize_default(default
=default
)(func
)
66 def inference_state_as_method_param_cache():
68 return _memoize_default(second_arg_is_inference_state
=True)(call
)
73 class CachedMetaClass(type):
75 This is basically almost the same than the decorator above, it just caches
76 class initializations. Either you do it this way or with decorators, but
77 with decorators you lose class access (isinstance, etc).
79 @inference_state_as_method_param_cache()
80 def __call__(self
, *args
, **kwargs
):
81 return super().__call
__(*args
, **kwargs
)
84 def inference_state_method_generator_cache():
86 This is a special memoizer. It memoizes generators and also checks for
87 recursion errors and returns no further iterator elemends in that case.
91 def wrapper(obj
, *args
, **kwargs
):
92 cache
= obj
.inference_state
.memoize_cache
94 memo
= cache
[function
]
96 cache
[function
] = memo
= {}
98 key
= (obj
, args
, frozenset(kwargs
.items()))
101 actual_generator
, cached_lst
= memo
[key
]
103 actual_generator
= function(obj
, *args
, **kwargs
)
105 memo
[key
] = actual_generator
, cached_lst
110 next_element
= cached_lst
[i
]
111 if next_element
is _RECURSION_SENTINEL
:
112 debug
.warning('Found a generator recursion for %s' % obj
)
113 # This means we have hit a recursion.
116 cached_lst
.append(_RECURSION_SENTINEL
)
117 next_element
= next(actual_generator
, None)
118 if next_element
is None:
121 cached_lst
[-1] = next_element