Solution 1

class Calculator(object):
def __init__(self):
self.cache = {}

def _find_cached_computation(self, operation, params):
if operation not in self.cache:
return None
for prev_computations in self.cache[operation]:
if prev_computations[:2] == params:
return prev_computations[2]

def _append_to_cache(self, operation, cached_value):
self.cache.setdefault(operation, [])
self.cache[operation].append(cached_value)

def add(self, x, y):
cached = self._find_cached_computation('add', (x, y))
if not cached:
cached = x + y
self._append_to_cache('add', (x, y, cached))
return cached

def subtract(self, x, y):
cached = self._find_cached_computation('subtract', (x, y))
if not cached:
cached = x - y
self._append_to_cache('subtract', (x, y, cached))
return cached

# Cached Calculator

Write a class Calculator that has the same methods as the previous one (add and subtract). But it should also have an attribute cache that keeps track of the operations being already performed as a dictionary (see example below and test cases). If the operation was already performed, the method should return the value from the cache. Example:

>>> c = Calculator()
>>> c.cache
{}
>>> c.add(2, 3)
5
>>> c.cache
{
'add': [
(2, 3, 5)
]
}
>>> c.subtract(8, 2)
6
>>> c.cache
{
'add': [
(2, 3, 5)
],
'subtract': [
(8, 2, 6)
]
}
>>> c.add(9, 3)
12
>>> c.cache
{
'add': [
(2, 3, 5),
(9, 3, 12)
],
'subtract': [
(8, 2, 6)
]
}
# Same method invoked again:
>>> c.add(2, 3)  # Should be returned from the cache
5

### Test Cases

test calculator cache -

def test_calculator_cache():
c = Calculator()
assert hasattr(c, 'cache')
assert c.cache == {}

assert c.add(2, 8) == 10
assert c.cache == {
'add': [
(2, 8, 10)
]
}

assert c.subtract(7, 2) == 5
assert c.cache == {
'add': [
(2, 8, 10)
],
'subtract': [
(7, 2, 5)
]
}

assert c.subtract(11, 7) == 4
assert c.cache == {
'add': [
(2, 8, 10)
],
'subtract': [
(7, 2, 5),
(11, 7, 4)
]
}

# Repeated operation. Should be cached
assert c.add(2, 8) == 10
assert c.cache == {
'add': [
(2, 8, 10)
],
'subtract': [
(7, 2, 5),
(11, 7, 4)
]
}
# empty