py_stepik/mod_oop/3.5_10_centralbank.py

159 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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()