Solution 1

```
class OperationInvalidException(Exception):
pass
class Operation(object):
def __init__(self, *args):
self.args = args
def operate(self):
raise NotImplementedError()
class AddOperation(Operation):
def operate(self):
return sum(self.args)
class SubtractOperation(Operation):
def operate(self):
if not self.args:
return 0
return self.args[0] - sum(self.args[1:])
class Calculator(object):
def __init__(self, operations):
self.operations = operations
def calculate(self, *args):
numbers, operation = args[:-1], args[-1]
if operation not in self.operations:
raise OperationInvalidException
operation = self.operations[operation](*numbers)
return operation.operate()
```

You'll need to build a better version of your calculator using OOP and inheritance:

A calculator can be built with different operations. An Operation is an abstract class for which you'll define

subclasses.

Example:

```
calc1 = Calculator(operations={
'add': AddOperation,
'subtract': SubtractOperation})
calc2 = Calculator(operations={
'add': AddOperation})
```

The calculator has 1 generic method `calculate`

that will receive the arguments

and the operation to perform. For example:

```
calc1.calculate(2, 1, 5, 'add') # Should return 2 + 1 + 5 = 8
calc2.calculate(1, 5, 'add') # Should return 1 + 5 = 6
```

*IMPORTANT: The number of arguments should be variable*

The Calculator will check if it has that computation present and

invoke the operation. Operations are initialized with the arguments to compute:

```
op = AddOperation(2, 1, 5)
```

Once you have an operation object created you should be able to invoke the `operate`

method PRESENT IN EVERY OPERATION.

```
op.operate() # Should return 8
```

*Important notes:*

* The only method that you must implement for every operation (descendant from Operation) is the operate method.
* If the operation is not supported by the calculator (for example invoking

`multiply`

on calc1) the calculator should raise a custom exception (built by you) named `OperationInvalidException`

.### Test Cases

test calculator with one operation - Run Test

```
def test_calculator_with_one_operation():
calc = Calculator(
operations={
'add': AddOperation
}
)
assert calc.calculate(1, 5, 13, 2, 'add') == 21
```

test calculator with multiple operations - Run Test

```
def test_calculator_with_multiple_operations():
calc = Calculator(
operations={
'add': AddOperation,
'subtract': SubtractOperation
}
)
assert calc.calculate(1, 5, 13, 2, 'add') == 21
assert calc.calculate(13, 3, 7, 'subtract') == 3
```

test subtract operation - Run Test

```
def test_subtract_operation():
op = SubtractOperation(10, 1, 3, 2, 1)
assert op.operate() == 3
```

test calculator invoked with an invalid operation - Run Test

```
import pytest
def test_calculator_invoked_with_an_invalid_operation():
calc = Calculator(
operations={
'add': AddOperation
}
)
with pytest.raises(OperationInvalidException, message='Expecting OperationInvalidException'):
res = calc.calculate(1, 5, 13, 2, 'INVALID')
```

test add operation - Run Test

```
def test_add_operation():
op = AddOperation(5, 1, 8, 3, 2)
assert op.operate() == 19
```