Koszyk zakupów (dataclass)

Zadanie 10 z 11 · rozdział 18Trudność: 2 z 3dataclassclassformatowanie

Treść zadania

Zaprojektuj dwie klasy danych (@dataclass):

  • Produkt z polami nazwa: str, cena: float, ilosc: int oraz metodą wartosc(), która zwraca $cena \cdot ilosc$.
  • Koszyk z polem produkty: list, które domyślnie jest pustą listą (field(default_factory=list)), oraz metodami:
    • dodaj(produkt) — jeśli w koszyku jest już produkt o tej samej nazwie, zwiększa jego ilość o produkt.ilosc; w przeciwnym razie dopisuje produkt na koniec listy,
    • suma() — zwraca łączną wartość wszystkich produktów,
    • wypisz_paragon() — wypisuje paragon (format poniżej).

Program wczytuje kolejne pozycje zakupów, dodaje je do koszyka i wypisuje paragon.

Dane wejściowe

  • 1. linia: liczba pozycji $n$
  • kolejne $n$ linii: nazwa cena ilosc — nazwa produktu (bez spacji), cena (liczba rzeczywista z co najwyżej 2 miejscami po przecinku) i ilość (liczba całkowita), oddzielone spacjami

Dane wyjściowe

  • Dla każdego produktu w koszyku (w kolejności pierwszego pojawienia się na wejściu) jedna linia, w której kolejno:
    • nazwa wyrównana do lewej w polu o szerokości 10 znaków,
    • spacja i ilość wyrównana do prawej w polu o szerokości 3,
    • x i cena z 2 miejscami po przecinku, wyrównana do prawej w polu o szerokości 7,
    • = i wartość z 2 miejscami po przecinku, wyrównana do prawej w polu o szerokości 8.
  • Linia złożona z 35 znaków -.
  • Linia Razem: <suma> (suma z 2 miejscami po przecinku).

Ograniczenia

  • $1 \le n \le 20$
  • Nazwa ma od 1 do 10 znaków. Produkty o tej samej nazwie mają zawsze tę samą cenę.
  • $0.01 \le cena \le 999.99$, $1 \le ilosc$, a łączna ilość każdego produktu w koszyku nie przekracza 99.

Uwagi

  • Dekorator @dataclass (z modułu dataclasses) sam tworzy konstruktor __init__, metodę __repr__ i porównanie __eq__ na podstawie pól zapisanych z adnotacją typu:
    ```python
    from dataclasses import dataclass
    @dataclass
    class Punkt:
    x: int
    y: int = 0
    p = Punkt(3)
    print(p) # Punkt(x=3, y=0)
    print(p == Punkt(3, 0)) # True
    `
  • Pole z listą nie może mieć wartości domyślnej [] — taka jedna lista byłaby wspólna dla wszystkich obiektów klasy (dlatego @dataclass zgłasza wtedy błąd). field(default_factory=list) sprawia, że każdy nowy obiekt dostaje własną, nową pustą listę.
  • Szerokość pola i wyrównanie ustawisz w f-stringu: f"{tekst:<10}" (do lewej, 10 znaków), f"{liczba:>3}" (do prawej, 3 znaki), f"{x:>8.2f}" (do prawej, 8 znaków, 2 miejsca po przecinku).

Przykład

Wejście
5
chleb 3.50 2
mleko 2.99 3
ser 12.00 1
chleb 3.50 1
jajka 0.89 10
Wyjście
chleb        3 x    3.50 =    10.50
mleko        3 x    2.99 =     8.97
ser          1 x   12.00 =    12.00
jajka       10 x    0.89 =     8.90
-----------------------------------
Razem: 40.37

Dwie pozycje chleb zostały scalone w jedną (2 + 1 = 3 sztuki).

Zasady obowiązujące w rozdziale 18

Zadania w tym rozdziale uczą projektowania klas: konstruktora __init__, atrybutów, metod, metod statycznych (@staticmethod) oraz metod specjalnych, dzięki którym obiekty można wypisywać (__str__), porównywać (__eq__) i łączyć operatorami (__add__, __sub__, __mul__, …). Ostatnie zadania wprowadzają też wyjątki (raise, try/except), klasy danych (@dataclass) i generatory (yield).

Konwencje wspólne:

  • Każde zadanie to osobny program: czyta standardowe wejście i wypisuje wynik na standardowe wyjście.
  • W każdym zadaniu dostajesz kod startowy ze szkieletem klasy, a zwykle także z gotowym wczytywaniem danych i wypisywaniem wyników. Twoim zadaniem jest przede wszystkim uzupełnienie metod klasy — wynik programu zależy od tego, jak działa Twoja klasa.
  • Nazwy klas i metod podane w treści są obowiązkowe (kod startowy z nich korzysta). Nazwy w kodzie piszemy bez polskich znaków, np. Kolo, Prostokat.
  • „Z dokładnością do 2 miejsc po przecinku” oznacza dokładnie dwie cyfry po kropce, np. f"{x:.2f}" (3.00, -0.50).
  • Program nie wypisuje komunikatów typu „Podaj liczbę:”.

Zadanie pochodzi z otwartego zbioru Nauka-Programowania (z rozwiązaniami wzorcowymi). Zgłoś błąd w treści lub testach.

Python uruchomi się w przeglądarce przy pierwszym teście.

Kod zapisuje się automatycznie w tej przeglądarce. Tab wstawia wcięcie; aby opuścić edytor klawiaturą, naciśnij Esc, a potem Tab.

Testy

Program dostaje „Wejście” przez input() i musi wypisać „Oczekiwane wyjście”. Liczby porównywane są z tolerancją 0,01, a tekst podany w input("…") nie jest sprawdzany.

Test 1

Nie uruchomiono
Wejście
1
woda 1.99 1
Oczekiwane wyjście
woda         1 x    1.99 =     1.99
-----------------------------------
Razem: 1.99

Test 2

Nie uruchomiono
Wejście
3
kawa 24.99 1
kawa 24.99 2
kawa 24.99 3
Oczekiwane wyjście
kawa         6 x   24.99 =   149.94
-----------------------------------
Razem: 149.94

Test 3

Nie uruchomiono
Wejście
4
banan 0.50 6
jablko 0.75 4
banan 0.50 6
gruszka 1.20 5
Oczekiwane wyjście
banan       12 x    0.50 =     6.00
jablko       4 x    0.75 =     3.00
gruszka      5 x    1.20 =     6.00
-----------------------------------
Razem: 15.00

Test 4

Nie uruchomiono
Wejście
3
czekolada 4.49 2
masło 7.99 1
czekolada 4.49 1
Oczekiwane wyjście
czekolada    3 x    4.49 =    13.47
masło        1 x    7.99 =     7.99
-----------------------------------
Razem: 21.46

Test 5

Nie uruchomiono
Wejście
2
telewizor 999.99 99
kabel 15.00 2
Oczekiwane wyjście
telewizor   99 x  999.99 = 98999.01
kabel        2 x   15.00 =    30.00
-----------------------------------
Razem: 99029.01
Uruchom z własnymi danymi