Skip to content

Commit a397fcf

Browse files
committed
remove require_hashable kwarg, improve docs
1 parent 5592d2b commit a397fcf

File tree

2 files changed

+24
-102
lines changed

2 files changed

+24
-102
lines changed

qiskit_experiments/framework/cache_method.py

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@
1717
import functools
1818

1919

20-
def cache_method(
21-
cache: Union[Dict, str] = "_cache", cache_args: bool = True, require_hashable: bool = True
22-
) -> Callable:
23-
"""Decorator for caching class instance methods.
20+
def cache_method(cache: Union[Dict, str] = "_cache", cache_args: bool = True) -> Callable:
21+
"""Decorator for caching regular methods in classes.
22+
23+
.. note::
24+
25+
When specifying a cache an existing dictionary value will be
26+
used as is. A string value will be used to check for an existing
27+
dict under that attribute name in the class instance.
28+
If the attribute is not present a new cache dict will be created
29+
and stored in that class instance.
2430
2531
Args:
26-
cache: The cache or cache attribute name to use. If a dict it will
27-
be used directly, if a str a cache dict will be created under
28-
that attribute name if one is not already present.
32+
cache: A dictionary or attribute name string to use as cache.
2933
cache_args: If True include method arg and kwarg values when
30-
caching the method. If False only a single return will
31-
be cached for the method regardless of any args.
32-
require_hashable: If True require all cached args and kwargs are
33-
hashable. If False un-hashable values are allowed
34-
but will be excluded from the cache key.
34+
matching cached values. These values must be hashable.
3535
3636
Returns:
3737
The decorator for caching methods.
3838
"""
3939
cache_fn = _cache_function(cache)
40-
cache_key_fn = _cache_key_function(cache_args, require_hashable)
40+
cache_key_fn = _cache_key_function(cache_args)
4141

4242
def cache_method_decorator(method: Callable) -> Callable:
4343
"""Decorator for caching method.
@@ -64,16 +64,14 @@ def _cached_method(self, *args, **kwargs):
6464
return cache_method_decorator
6565

6666

67-
def _cache_key_function(cache_args: bool, require_hashable: bool) -> Callable:
67+
def _cache_key_function(cache_args: bool) -> Callable:
6868
"""Return function for generating cache keys.
6969
7070
Args:
7171
cache_args: If True include method arg and kwarg values when
72-
caching the method. If False only a single return will
73-
be cached for the method regardless of any args.
74-
require_hashable: If True require all cached args and kwargs are
75-
hashable. If False un-hashable values are allowed
76-
but will be excluded from the cache key.
72+
caching the method. If False all calls to the instances
73+
method will return the same cached value regardless of
74+
any arg or kwarg values.
7775
7876
Returns:
7977
The functions for generating cache keys.
@@ -84,28 +82,10 @@ def _cache_key(*args, **kwargs):
8482
# pylint: disable = unused-argument
8583
return tuple()
8684

87-
elif require_hashable:
88-
89-
def _cache_key(*args, **kwargs):
90-
return args + tuple(list(kwargs.items()))
91-
9285
else:
9386

9487
def _cache_key(*args, **kwargs):
95-
cache_key = tuple()
96-
for arg in args:
97-
try:
98-
hash(arg)
99-
except TypeError:
100-
continue
101-
cache_key += (arg,)
102-
for key, value in kwargs.items():
103-
try:
104-
hash(value)
105-
except TypeError:
106-
continue
107-
cache_key += ((key, value),)
108-
return cache_key
88+
return args + tuple(list(kwargs.items()))
10989

11090
return _cache_key
11191

@@ -114,9 +94,9 @@ def _cache_function(cache: Union[Dict, str]) -> Callable:
11494
"""Return function for initializing and accessing cache dict.
11595
11696
Args:
117-
cache: The cache or cache attribute name to use. If a dict it will
118-
be used directly, if a str a cache dict will be created under
119-
that attribute name if one is not already present.
97+
cache: The dictionary or cache attribute name to use. If a dict it
98+
will be used directly, if a str a cache dict will be created
99+
under that attribute name if one is not already present.
120100
121101
Returns:
122102
The function for accessing the cache dict.

test/framework/test_cache_method.py

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ def method(self, *args, **kwargs):
8989
self.assertEqual(obj.method(i), ret, msg="method didn't return cached value")
9090
self.assertEqual(obj.method_calls, 1, msg="Cached method was not evaluated once")
9191

92-
def test_require_hashable_raises(self):
93-
"""Test require_hashable=True"""
92+
def test_non_hashable_raises(self):
93+
"""Test non hashable args raise"""
9494

9595
class CachedClass:
9696
"""Class with cached method"""
9797

98-
@cache_method(require_hashable=True)
98+
@cache_method()
9999
def method(self, *args, **kwargs):
100100
"""Test method for caching"""
101101
return args, kwargs
@@ -104,64 +104,6 @@ def method(self, *args, **kwargs):
104104
self.assertRaises(TypeError, obj.method, [1, 2, 3])
105105
self.assertRaises(TypeError, obj.method, kwarg=[1, 2, 3])
106106

107-
def test_require_hashable_false_args(self):
108-
"""Test require_hashable=False matches hashable args"""
109-
110-
class CachedClass:
111-
"""Class with cached method"""
112-
113-
def __init__(self):
114-
self.method_calls = 0
115-
116-
@cache_method(require_hashable=False)
117-
def method(self, *args, **kwargs):
118-
"""Test method for caching"""
119-
self.method_calls += 1
120-
return args, kwargs
121-
122-
obj = CachedClass()
123-
hashable = [1, 2, 3]
124-
non_hashable = [[i] for i in hashable]
125-
126-
cached_vals = [obj.method(i) for i in hashable]
127-
for i, val in zip(hashable, cached_vals):
128-
for j in non_hashable:
129-
self.assertEqual(obj.method(i, j), val)
130-
self.assertEqual(
131-
obj.method_calls,
132-
len(hashable),
133-
msg="Cached method was not evaluated once per hashable arg",
134-
)
135-
136-
def test_require_hashable_false_kwargs(self):
137-
"""Test require_hashable=False matches hashable kwargs"""
138-
139-
class CachedClass:
140-
"""Class with cached method"""
141-
142-
def __init__(self):
143-
self.method_calls = 0
144-
145-
@cache_method(require_hashable=False)
146-
def method(self, *args, **kwargs):
147-
"""Test method for caching"""
148-
self.method_calls += 1
149-
return args, kwargs
150-
151-
obj = CachedClass()
152-
hashable = [1, 2, 3]
153-
non_hashable = [[i] for i in hashable]
154-
155-
cached_vals = [obj.method(a=i) for i in hashable]
156-
for i, val in zip(hashable, cached_vals):
157-
for j in non_hashable:
158-
self.assertEqual(obj.method(a=i, b=j), val)
159-
self.assertEqual(
160-
obj.method_calls,
161-
len(hashable),
162-
msg="Cached method was not evaluated once per hashable arg",
163-
)
164-
165107
def test_cache_name(self):
166108
"""Test decorator with a custom cache name"""
167109

0 commit comments

Comments
 (0)