• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Implementacja klasy Money w Pythonie

Object Storage Arubacloud
0 głosów
212 wizyt
pytanie zadane 3 czerwca 2023 w Python przez whiteman808 Obywatel (1,820 p.)
edycja 3 czerwca 2023 przez whiteman808

Witam

Co sądzicie o takiej implementacji klasy Money?

import functools
from decimal import Decimal
from dataclasses import dataclass


@dataclass
class Currency:
    name: str
    symbol: str
    exchange_rate: Decimal

    def __str__(self) -> str:
        return self.name

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self.name!r}, {self.symbol!r}, {self.exchange_rate!r})"


@functools.total_ordering
class Money:
    def __init__(self, amount: Decimal, currency: Currency, precision: int = 2):
        self._amount = amount
        self.precision = precision
        self.currency = currency

    @property
    def amount(self) -> Decimal:
        return self._amount.quantize(Decimal("10") ** -self.precision)

    @amount.setter
    def amount(self, value: Decimal) -> None:
        self._amount = value

    def __str__(self) -> str:
        return f"{self.amount} {self.currency.symbol}"

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self.amount!r}, {self.currency!r})"

    def __add__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot add two {self.__class__.__name__} instances: "
                             "different currency property")
        return Money(self.amount + other.amount, self.currency)

    def __sub__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot subtract two {self.__class__.__name__} instances: "
                             "different currency property")
        return Money(self.amount - other.amount, self.currency)

    def __mul__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot multiply two {self.__class__.__name__} instances: "
                             "different currency property")
        return Money(self.amount * other.amount, self.currency, self.precision)

    def __truediv__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot divide two {self.__class__.__name__} instances: "
                             "different currency property")
        return Money(self.amount / other.amount, self.currency, self.precision)

    def __iadd__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot add two {self.__class__.__name__} instances: "
                             "different currency property")
        self.amount += other.amount
        return self

    def __isub__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot subtract two {self.__class__.__name__} instances: "
                             "different currency property")
        self.amount -= other.amount
        return self

    def __imul__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot multiply two {self.__class__.__name__} instances: "
                             "different currency property")
        self.amount *= other.amount
        return self

    def __idiv__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot divide two {self.__class__.__name__} instances: "
                             "different currency property")
        self.amount /= other.amount
        return self

    def __lt__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot compare two {self.__class__.__name__} instances: "
                             "different currency property")
        return self.amount < other.amount

    def __eq__(self, other):
        if self.currency != other.currency:
            raise ValueError(f"cannot compare two {self.__class__.__name__} instances: "
                             "different currency property")
        return self.amount == other.amount

Testy:

import pytest
from example_shop.shop.money import Currency, Money
from decimal import Decimal
from typing import Optional


def money_euro(amount: str, precision: Optional[int] = None) -> Money:
    if precision is None:
        return Money(Decimal(amount), Currency("Euro", "EUR", Decimal("4.52")))
    return Money(Decimal(amount), Currency("Euro", "EUR", Decimal("4.52")), precision)


def money_usd(amount: str, precision: Optional[int] = None) -> Money:
    if precision is None:
        return Money(Decimal(amount), Currency("American dollar", "USD", Decimal("4.17")))
    return Money(Decimal(amount), Currency("American dollar", "USD", Decimal("4.17")), precision)


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("2.7")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("4.0")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("4.00000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("3", 4)),
                          (money_usd("-1.5", 4), money_usd("3", 5), money_usd("1.5", 5))])
def test_money_add_the_same_currency(price1, price2, expected):
    assert price1 + price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("0.3")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("1.4")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("1.40000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("3", 5), money_usd("-1.5", 5))])
def test_money_subtract_the_same_currency(price1, price2, expected):
    assert price1 - price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.8")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("3.51")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("3.51000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-4.5", 5))])
def test_money_multiply_the_same_currency(price1, price2, expected):
    assert price1 * price2 == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.25")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("2.08")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("2.08000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-0.5", 5))])
def test_money_divide_the_same_currency(price1, price2, expected):
    assert price1 / price2 == expected


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_add_the_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 + price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_subtract_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 - price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_multiply_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 * price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_divide_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 / price2


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("2.7")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("4.0")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("4.00000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("3", 4)),
                          (money_usd("-1.5", 4), money_usd("3", 5), money_usd("1.5", 5))])
def test_money_add_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result += price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("0.3")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("1.4")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("1.40000")),
                          (money_usd("1.5", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("3", 5), money_usd("-1.5", 5))])
def test_money_subtract_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result -= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.8")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("3.51")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("3.51000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-4.5", 5))])
def test_money_multiply_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result *= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_usd("1.5"), money_usd("1.2"), money_usd("1.25")),
                          (money_usd("2.7"), money_usd("1.3"), money_usd("2.08")),
                          (money_usd("2.700"), money_usd("1.3"), money_usd("2.08000")),
                          (money_usd("0", 4), money_usd("1.5", 3), money_usd("0", 4)),
                          (money_usd("1.5", 4), money_usd("-3", 5), money_usd("-0.5", 5))])
def test_money_divide_in_place_the_same_currency(price1, price2, expected):
    result = price1
    result /= price2
    assert result == expected


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_add_in_place_the_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result += price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_subtract_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result -= price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_multiply_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result *= price2
        assert result


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.5"), money_euro("1.2")),
                          (money_euro("1.2"), money_usd("1.5")),
                          (money_usd("1.5", 4), money_euro("1.2", 5)),
                          (money_euro("1.2", 4), money_usd("1.5", 5))])
def test_money_divide_in_place_different_currency(price1, price2):
    with pytest.raises(ValueError):
        result = price1
        result /= price2
        assert result

@pytest.mark.parametrize("price1,price2,expected",
                         [(money_euro("1.23"), money_euro("4.56"), True),
                          (money_euro("1.5"), money_euro("1"), False),
                          (money_usd("-2"), money_usd("0"), True),
                          (money_euro("0"), money_euro("0"), False)])
def test_less_than_the_same_currency(price1, price2, expected):
    assert (price1 < price2) == expected


@pytest.mark.parametrize("price1,price2,expected",
                         [(money_euro("1.23"), money_euro("4.56"), False),
                          (money_euro("1.5"), money_euro("1"), False),
                          (money_usd("-2"), money_usd("0"), False),
                          (money_euro("0"), money_euro("0"), True)])
def test_equal_the_same_currency(price1, price2, expected):
    assert (price1 == price2) == expected


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.23"), money_euro("4.56")),
                          (money_euro("1.5"), money_usd("1"))])
def test_less_than_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 < price2


@pytest.mark.parametrize("price1,price2",
                         [(money_usd("1.23"), money_euro("4.56")),
                          (money_euro("1.5"), money_usd("1"))])
def test_equal_different_currency(price1, price2):
    with pytest.raises(ValueError):
        assert price1 == price2

Czy uzależnianie od siebie kolejności przypisań jest ok?

Z góry dziękuję i pozdrawiam :-)

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
2 odpowiedzi 1,298 wizyt
pytanie zadane 3 kwietnia 2019 w Python przez Q7V Gaduła (4,250 p.)
0 głosów
1 odpowiedź 138 wizyt
pytanie zadane 11 marca 2016 w Python przez ZakosiliMiNeta Nałogowiec (30,870 p.)
+1 głos
1 odpowiedź 117 wizyt

92,555 zapytań

141,403 odpowiedzi

319,560 komentarzy

61,940 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...