Solution 1

``````class Currency(object):
USD_CONVERSIONS = {
'CHF': 0.95,
'CAD': 1.28,
'GBP': 0.72,
'JPY': 106.80,
'EUR': 0.81,
'USD': 1.0,
'MXN': 18.62,
'ARG': 20.24,
'AUD': 1.27
}
BASE_UNIT = 'USD'

def __init__(self, value, unit="USD"):
if unit not in self.USD_CONVERSIONS:
raise ValueError('Invalid Unit {}'.format(unit))
self.value = value
self.unit = unit

def convert(self, target_unit):
if target_unit not in self.USD_CONVERSIONS:
raise ValueError('Invalid Unit {}'.format(target_unit))
self_usd = self.value / self.USD_CONVERSIONS[self.unit]
target_usd = self_usd * self.USD_CONVERSIONS[target_unit]
return Currency(round(target_usd, 4), target_unit)

def __str__(self):
return '{}\${}'.format(self.unit, self.value)

def __repr__(self):
return "Currency({}, '{}')".format(self.value, self.unit)

def __eq__(self, other):
self_usd = self.convert(self.BASE_UNIT)
other_usd = other.convert(self.BASE_UNIT)
return self_usd.value == other_usd.value

def __ne__(self, other):
return not self == other

def __lt__(self, other):
self_usd = self.convert(self.BASE_UNIT)
other_usd = other.convert(self.BASE_UNIT)
return self_usd.value < other_usd.value

def __le__(self, other):
return self < other or self == other

def __gt__(self, other):
return other < self

def __ge__(self, other):
return self > other or self == other

def __add__(self, other):
self_usd = self.convert(self.BASE_UNIT)
other_usd = other.convert(self.BASE_UNIT)
return Currency(self_usd.value + other_usd.value, self.BASE_UNIT)

def __iadd__(self, other):
total_usd = self + other
total_self_unit = total_usd.convert(self.unit)
return Currency(total_self_unit.value, self.unit)

def __radd__(self, other):
return self + Currency(other, 'USD')

def __sub__(self, other):
pass

def __isub__(self, other):
pass

def __rsub__(self, other):
pass
``````

# Currency My Way Out

Complete the methods for the class `Currency` so that it has functionality to compare and perform basic math operations on Currencies.

You can see in the editor that we have a dictionary containing the exchange rates of many currencies (`MXN`, `EUR`, `ARG`, etc). But they're all expressed in US Dollars (`USD`). So, how do you convert from (for example) `EUR` > `MXN`? Well, you have to go through `USD` first. The `convert` method takes any pair of currencies (as in our previous example `EUR` > `MXN`) and converts them (`EUR` to `MXN`). As we mentioned, you have to convert it to `USD` first, so the pseudocode looks something like:

``````# Objective: EUR > MXN
# Step 1: Convert EUR > USD (store value in EUR_usd)
# Step 2: Convert EUR_usd to > MXN
``````

Probably makes more sense in this example:

``````# 4 EUR to MXN
# 4 / 0.81 (EUR conversion to USD) * 18.62 (MXN conversion from USD) == 91.95
``````

This has similar magic methods to the Distance assignment, but now there additional ones to teach your class how to be added and (optionally) subtracted.

Use this guide for magic method guidance, and read the tests if you get stuck.

IMPORTANT: To avoid rounding issues, round your conversion up to 4 decimals (`round(value, 4)`). For example, to convert from `AUD` > `USD` you have to do (`1 / 1.27`) which yields a result with too many decimals: `0.7874015748031495`. Round it to 4 decimals to get the expected result: `round(0.7874015748031495, 4) == 0.7874`.

### Test Cases

test convert target to target -

``````def test_convert_target_to_target():
c1 = Currency(1, 'AUD')
c2 = Currency(1, 'EUR')

aud_to_eur = c1.convert('EUR')
aud_to_mxn = c1.convert('MXN')
eur_to_jpy = c2.convert('JPY')
eur_to_arg = c2.convert('ARG')

assert aud_to_eur.value == 0.6378
assert aud_to_mxn.value == 14.6614
assert eur_to_jpy.value == 131.8519
assert eur_to_arg.value == 24.9877
``````

test general comparison -

``````def test_general_comparison():
c1 = Currency(1.27, 'AUD')
c2 = Currency(1, 'USD')
c3 = Currency(100, 'CAD')

assert c3 > c1
assert c1 < c3

# Same for c2
assert c3 > c2
assert c2 < c3

assert c2 >= c1
assert c2 <= c1
assert c1 >= c2
assert c1 <= c2

assert c3 >= c1
assert c1 <= c3
assert c3 >= c2
assert c2 <= c3
``````

test convert invalid units -

``````import pytest

def test_convert_invalid_units():
c1 = Currency(1, 'USD')

with pytest.raises(ValueError):
c1.convert('NOT-FOUND')
``````

test convert usd to target -

``````def test_convert_usd_to_target():
c1 = Currency(1, 'USD')

in_aud = c1.convert('AUD')
in_mxn = c1.convert('MXN')
in_eur = c1.convert('EUR')
in_usd = c1.convert('USD')

assert in_aud.value == 1.27
assert in_mxn.value == 18.62
assert in_eur.value == 0.81
assert in_usd.value == 1  # Same
``````

test str repr -

``````def test_str_repr():
c1 = Currency(1.1, 'CAD')
c2 = Currency(2.5, 'USD')
c3 = Currency(150, 'MXN')

assert str(c1) == 'CAD\$1.1'
assert str(c2) == 'USD\$2.5'
assert str(c3) == 'MXN\$150'

assert repr(c1) == "Currency(1.1, 'CAD')"
assert repr(c2) == "Currency(2.5, 'USD')"
assert repr(c3) == "Currency(150, 'MXN')"
``````

test add methods -

``````def test_add_methods():
c1 = Currency(1, 'AUD')
c2 = Currency(5, 'USD')
c3 = Currency(100, 'CAD')
c4 = Currency(2, 'USD')

assert c1 + c2 == Currency(5.7874, 'USD')
assert c1 + c3 == Currency(78.9124, 'USD')
assert c2 + c3 == Currency(83.125, 'USD')
assert c2 + c4 == Currency(7, 'USD')

# __radd__
assert 4 + c2 == Currency(9, 'USD')
assert 1 + c3 == Currency(79.125, 'USD')

# __iadd__
c1 += Currency(3, 'AUD')
assert c1.value == 4
assert c1.unit == 'AUD'

c2 += Currency(100, 'MXN')
assert c2.value == 10.3706
assert c2.unit == 'USD'
``````

test convert target to usd -

``````def test_convert_target_to_usd():
c1 = Currency(1, 'AUD')
c2 = Currency(1, 'MXN')
c3 = Currency(1, 'EUR')
c4 = Currency(1, 'JPY')
c5 = Currency(1, 'USD')

c1_to_usd = c1.convert('USD')
c2_to_usd = c2.convert('USD')
c3_to_usd = c3.convert('USD')
c4_to_usd = c4.convert('USD')
c5_to_usd = c5.convert('USD')

assert c1_to_usd.value == 0.7874
assert c2_to_usd.value == 0.0537
assert c3_to_usd.value == 1.2346
assert c4_to_usd.value == 0.0094
assert c5_to_usd.value == 1
``````

test valid units -

``````import pytest

def test_valid_units():
# We can't create a Currency with a unit not present in the
# USD_conversions dict
with pytest.raises(ValueError):
Currency(1, 'NOT-FOUND')
``````

test compare equality -

``````def test_compare_equality():
c1 = Currency(1.27, 'AUD')
c2 = Currency(1, 'USD')
c3 = Currency(2, 'CAD')

assert c1 == c2
assert c1 != c3
assert c2 != c3
``````
class Currency(object): USD_CONVERSIONS = { 'CHF': 0.95, 'CAD': 1.28, 'GBP': 0.72, 'JPY': 106.80, 'EUR': 0.81, 'USD': 1.0, 'MXN': 18.62, 'ARG': 20.24, 'AUD': 1.27 } def __init__(self, value, unit="USD"): pass def convert(self, target_unit): pass def __str__(self): pass def __repr__(self): pass def __eq__(self, other): pass def __ne__(self, other): pass def __lt__(self, other): pass def __le__(self, other): pass def __gt__(self, other): pass def __ge__(self, other): pass def __add__(self, other): pass def __iadd__(self, other): pass def __radd__(self, other): pass def __sub__(self, other): # Optional. Not tested pass def __isub__(self, other): # Optional. Not tested pass def __rsub__(self, other): # Optional. Not tested pass