from dataclasses import dataclass CURRENCY_SYMBOLS = { "RUB": "₽", "USD": "$", "EUR": "€", "GBP": "£", "CNY": "¥", "JPY": "¥", "KZT": "₸", "UAH": "₴", } @dataclass class ExchangeRate: from_currency: str = "USD" to_currency: str = "RUB" rate: float = 80.0 def to_dict(self) -> dict: return { "from_currency": self.from_currency, "to_currency": self.to_currency, "rate": self.rate, } @classmethod def from_dict(cls, data: dict) -> "ExchangeRate": return cls( from_currency=data.get("from_currency", "USD"), to_currency=data.get("to_currency", "RUB"), rate=data.get("rate", 80.0), ) DEFAULT_RATES: list[ExchangeRate] = [ ExchangeRate(from_currency="USD", to_currency="RUB", rate=80.0), ] class CurrencyError(Exception): pass class CurrencyConverter: def __init__(self, rates: list[ExchangeRate] | None = None): self._rates: dict[tuple[str, str], float] = {} if rates: for r in rates: self.set_rate(r.from_currency, r.to_currency, r.rate) def set_rate(self, from_currency: str, to_currency: str, rate: float) -> None: if rate <= 0: raise CurrencyError(f"Rate must be positive: {rate}") self._rates[(from_currency, to_currency)] = rate inverse = 1.0 / rate self._rates[(to_currency, from_currency)] = inverse def get_rate(self, from_currency: str, to_currency: str) -> float: if from_currency == to_currency: return 1.0 try: return self._rates[(from_currency, to_currency)] except KeyError: raise CurrencyError(f"No exchange rate: {from_currency} → {to_currency}") def convert(self, amount: float, from_currency: str, to_currency: str) -> float: if from_currency == to_currency: return amount rate = self.get_rate(from_currency, to_currency) return round(amount * rate, 2) def get_symbol(self, currency: str) -> str: return CURRENCY_SYMBOLS.get(currency, currency) @classmethod def with_defaults(cls) -> "CurrencyConverter": return cls(DEFAULT_RATES)