"""Dictionary of supported currencies as instances of a Currency class. Face values are stored as decimals*. * Decimal is used to avoid problems of rounding float numbers. So first two digits to the right are used to store a fraction part. You can simply divide this number by 100 to get a floating-point number. """ from __future__ import annotations from operator import mul from typing import Dict, List, TypedDict __all__ = [ "Currency", "CURRENCIES", "BaseCurrencyError", "CurrencyIsNotSupportedError", "CurrenciesCoinCountMismatchError", "CurrencyMismatchError"] class BaseCurrencyError(Exception): """Base class for all currency exeptions.""" def __init__(self, message=None, *args, **kwargs) -> None: if message is None: message = self.__doc__ super().__init__(message, *args, **kwargs) class CurrencyIsNotSupportedError(BaseCurrencyError): """Currency is not supported.""" pass class CurrencyMismatchError(BaseCurrencyError): """Currencies doesn't match, but they must do so.""" def __init__(self, extra=None): if not extra is None: super().__init__(f"{self.__doc__} {extra}") else: super().__init__(self.__doc__) class CurrenciesCoinCountMismatchError(BaseCurrencyError): """Count of coins of a new currency and an old one should be equal.""" pass class Currency: """Currency's representation and methods to work with it. Arguments: iso_code -- ISO code of a currency; name -- full name of a currency; description -- description of a currency; coins_count -- number of face values of coins; coin_names -- list of names of face values (e.g. 1c, 5c); face_values -- list of values for each face value in decimal (e.g. $476.40 is 47640); """ def __init__(self, iso_code: str, name: str, description: str, coins_count: int, coin_names: List[str], face_values: List[int]): self._iso = iso_code.upper() self._name = name self._description = description self._coins_count = coins_count self._coin_names = coin_names self._face_values = face_values @property def iso(self) -> str: """ISO code of a currency.""" return self._iso @property def name(self) -> str: """Full name of a currency.""" return self._name @property def description(self) -> str: """Description of a currency (e.g. country).""" return self._description @property def count(self): """Count of coins of a currency.""" return self._coins_count @property def coin_names(self) -> List[str]: """Names of coins (usually face values, e.g. 25¢). From least to most.""" return self._coin_names @property def face_values(self) -> List[int]: """Face values of coins. From least to most.""" return self._face_values def multiply(self, lst: List[int]) -> List[float]: """Multiplies a list by face values. Length of list must match currency's coins count.""" if len(lst) != len(self): raise CurrenciesCoinCountMismatchError return list(map(mul, lst, self.face_values)) @staticmethod def from_string(currency: str) -> Currency: """Creates a Currency object from its string representation.""" iso, name, desc, ccnt, fvn, fv = currency.split(";") return Currency(iso, name, desc, int(ccnt), fvn.split(","), list(map(int, fv.split(",")))) def __mul__(self, lst: List[int]) -> List[float]: return self.multiply(lst) def __len__(self) -> int: return self.count def __str__(self): return f"{self.iso};{self.name};{self.description};" \ f"{self.count};{','.join(self.coin_names)};" \ f"{','.join(list(map(str, self.face_values)))}" CURRENCIES: Dict[str, Currency] = { "RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8, ["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], [1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]), "SRUB": Currency("SRUB", "Russian ruble (shortened)", "Russian Federation. Excluding coins of 1 and 5 kopek.", 6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], [10, 50, 1_00, 2_00, 5_00, 10_00]), "BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8, ["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."], [1, 2, 5, 10, 20, 50, 1_00, 2_00]), "UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10, ["1к.", "2к.", "5к.", "10к.", "25к.", "50к.", "₴1", "₴2", "₴5", "₴10"], [1, 2, 5, 10, 25, 50, 1_00, 2_00, 5_00, 10_00]), "USD": Currency("USD", "US Dollar", "United States of America", 6, ["1¢", "5¢", "10¢", "25¢", "50¢", "$1"], [1, 5, 10, 25, 50, 1_00]), "EUR": Currency("EUR", "Euro", "European Union", 8, ["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"], [1, 2, 5, 10, 20, 50, 1_00, 2_00]), "GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9, ["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"], [1, 2, 5, 10, 20, 25, 50, 1_00, 2_00]) }