diff --git a/mod_oop/3.5_10_centralbank.py b/mod_oop/3.5_10_centralbank.py new file mode 100644 index 0000000..335096e --- /dev/null +++ b/mod_oop/3.5_10_centralbank.py @@ -0,0 +1,158 @@ +""" + https://stepik.org/lesson/701990/step/10?unit=702091 + +В программе необходимо объявить классы для работы с кошельками в разных валютах: +MoneyR - для рублевых кошельков +MoneyD - для долларовых кошельков +MoneyE - для евро-кошельков + +Объекты этих классов могут создаваться командами: +rub = MoneyR() # с нулевым балансом +dl = MoneyD(1501.25) # с балансом в 1501.25 долларов +euro = MoneyE(100) # с балансом в 100 евро +>>> rub = MoneyR() +>>> dl = MoneyD(1501.25) +>>> euro = MoneyE(100) +>>> rub.volume, dl.volume, euro.volume +(0, 1501.25, 100) + +В каждом объекте этих классов должны формироваться локальные атрибуты: +__cb - ссылка на класс CentralBank (центральный банк, изначально None); +__volume - объем денежных средств в кошельке (если не указано, то 0). + +Также в классах MoneyR, MoneyD и MoneyE должны быть объекты-свойства (property) для работы с локальными атрибутами: +cb - для изменения и считывания данных из переменной __cb; +volume - для изменения и считывания данных из переменной __volume. + +Объекты классов должны поддерживать следующие операторы сравнения: +rub < dl +dl >= euro +rub == euro # значения сравниваются по текущему курсу центрального банка с погрешностью 0.1 (плюс-минус) +euro > rub +При реализации операторов сравнения считываются соответствующие значения __volume из сравниваемых объектов и приводятся к рублевому эквиваленту в соответствии с курсом валют центрального банка. + +Чтобы каждый объект классов MoneyR, MoneyD и MoneyE "знал" текущие котировки, необходимо в программе объявить еще один класс CentralBank. +Объекты класса CentralBank создаваться не должны (запретить), при выполнении команды: +>>> CentralBank() + +должно просто возвращаться значение None. А в самом классе должен присутствовать атрибут: + +rates = {'rub': 72.5, 'dollar': 1.0, 'euro': 1.15} +>>> CentralBank.rates +{'rub': 72.5, 'dollar': 1.0, 'euro': 1.15} + +Здесь числа (в значениях словаря) - курс валюты по отношению к доллару. + +Также в CentralBank должен быть метод уровня класса: +register(cls, money) - для регистрации объектов классов MoneyR, MoneyD и MoneyE. + +При регистрации значение __cb объекта money должно ссылаться на класс CentralBank. +Через эту переменную объект имеет возможность обращаться к атрибуту rates класса CentralBank и брать нужные котировки. + +Если кошелек не зарегистрирован, то при операциях сравнения должно генерироваться исключение: +raise ValueError("Неизвестен курс валют.") +Пример использования классов (эти строчки в программе писать не нужно): + +>>> CentralBank.rates = {'rub': 72.5, 'dollar': 1.0, 'euro': 1.15} +>>> r = MoneyR(45000) +>>> d = MoneyD(500) +>>> CentralBank.register(r) +>>> CentralBank.register(d) +>>> r > d, r.value, d.value +(True, 620.6896551724138, 500.0) + +if r > d: + print("неплохо") +else: + print("нужно поднажать") + +>>> r2 = MoneyR(100) +>>> r2 > r +Traceback (most recent call last): + ... +ValueError: Неизвестен курс валют. + +P.S. В программе на экран ничего выводить не нужно, только объявить классы. +""" + +from functools import total_ordering + + +class CentralBank: + rates = {"rub": 72.5, "dollar": 1.0, "euro": 1.15} + + def __new__(self): + ... + + @classmethod + def register(cls, obj): + obj.register_cb(cls) + + +@total_ordering +class Wallet: + currency: str + + def __repr__(self): + return f"{self.__class__.__name__}({self.volume!r})" + + def register_cb(self, cb: type): + self.cb = cb + + def __init__(self, volume=0): + self.cb = None + self.volume = volume + + def __eq__(self, other): + if hasattr(other, "value"): + return abs(self.value - other.value) <= 0.1 + return NotImplemented + + def __lt__(self, other): + if hasattr(other, "value"): + return abs(self.value - other.value) > 0.1 and self.value < other.value + return NotImplemented + + @property + def cb(self): + return getattr(self, f"_{self.__class__.__name__}__cb") + + @cb.setter + def cb(self, value): + setattr(self, f"_{self.__class__.__name__}__cb", value) + + @property + def volume(self): + return getattr(self, f"_{self.__class__.__name__}__volume") + + @volume.setter + def volume(self, value): + setattr(self, f"_{self.__class__.__name__}__volume", value) + + @property + def rate(self): + if not self.cb: + raise ValueError("Неизвестен курс валют.") + return self.cb.rates[self.currency] + + @property + def value(self): + return self.volume / self.rate + + +class MoneyR(Wallet): + currency = "rub" + + +class MoneyD(Wallet): + currency = "dollar" + + +class MoneyE(Wallet): + currency = "euro" + + +if __name__ == "__main__": + import doctest + + doctest.testmod()