← Lekcje
Python · lekcja 29

Klasy i obiekty 2: lista obiektów, __str__ i dziedziczenie

  1. Lista obiektów i pętla, która woła metodę
  2. Metoda __str__: obiekt wypisany po ludzku
  3. Obiekt w środku innego obiektu
  4. Dziedziczenie: nowa klasa na bazie starej
Rozgrzewka · 3 minuty

Co pamiętacie z lekcji 28?

  1. Czym klasa różni się od obiektu?
  2. Co robi metoda __init__?
  3. Po co w metodzie stoi słowo self?
Część 1 · Lista obiektów

W liście mogą siedzieć całe obiekty

students = [
    Student("Ala", [5, 4, 3]),
    Student("Bartek", [3, 3, 4]),
    Student("Celina", [5, 5, 4]),
]

for student in students:
    print(student.name, round(student.average(), 2))
Ala 4.0
Bartek 3.33
Celina 4.67
  • Lista trzyma trzy obiekty klasy Student.
  • Zmienna student w pętli to za każdym obrotem inny obiekt.
students 0 · obiekt Student Ala · [5, 4, 3] 1 · obiekt Student Bartek · [3, 3, 4] 2 · obiekt Student Celina · [5, 5, 4]
docs.python.org, The Python Tutorial, rozdz. 9.3.3 (Instance Objects)
Część 1 · Rekord w liście obiektów

Ten sam wzorzec co szukanie największej liczby

best = students[0]
for student in students:
    if student.average() > best.average():
        best = student

print(best.name, round(best.average(), 2))
Celina 4.67
  • best to rekord: najlepszy obiekt z dotychczas obejrzanych.
  • Startuje od pierwszego ucznia, bo średniej nie da się porównać z zerem.
  • Gdy nowy obiekt ma wyższą średnią, zostaje nowym rekordem.
  • Na końcu w best siedzi cały obiekt, więc znamy też imię.
docs.python.org, The Python Tutorial, rozdz. 9.3.4 (Method Objects)
Część 2 · Metoda __str__

Obiekt wypisany po ludzku

Bez __str__

ala = Student("Ala", [5, 4, 3])
print(ala)
<__main__.Student object at 0x105e06120>

Z __str__ w klasie Student

def __str__(self):
    return f"{self.name}: {round(self.average(), 2)}"
Ala: 4.0

Bez tej metody print wypisuje nazwę klasy i adres obiektu w pamięci, czyli numer, który za każdym uruchomieniem jest inny. Metoda __str__ oddaje napis, który ma się pokazać zamiast tego adresu. print woła ją sam, nie trzeba jej wywoływać.

Skąd nazwa: metody z dwoma podkreślnikami z obu stron Python woła sam w odpowiednim momencie. Tak samo __init__ uruchamia się przy tworzeniu obiektu, a __str__ przy wypisywaniu.

docs.python.org, The Python Language Reference, rozdz. 3.3.1 (Basic customization, object.__str__)
Część 3 · Obiekt w obiekcie

Atrybutem może być lista innych obiektów

class SchoolClass:
    def __init__(self, name):
        self.name = name
        self.students = []

    def add_student(self, student):
        self.students.append(student)

    def best_student(self):
        best = self.students[0]
        for student in self.students:
            if student.average() > best.average():
                best = student
        return best
group = SchoolClass("2A")
group.add_student(Student("Ala", [5, 4, 3]))
group.add_student(Student("Bartek", [3, 3, 4]))
group.add_student(Student("Celina", [5, 5, 4]))

print(group.name, len(group.students))
print(group.students[1].name)
print(group.best_student())
2A 3
Bartek
Celina: 4.67

Zapis group.students[1].name czytamy od lewej: obiekt klasy, jego lista uczniów, drugi uczeń, imię tego ucznia.

docs.python.org, The Python Tutorial, rozdz. 9.3.5 (Class and Instance Variables)
Część 4 · Dziedziczenie

Nowa klasa na bazie starej

class Athlete(Student):
    def __init__(self, name, grades, sport):
        super().__init__(name, grades)
        self.sport = sport

    def __str__(self):
        score = round(self.average(), 2)
        return f"{self.name} ({self.sport}): {score}"

dawid = Athlete("Dawid", [4, 4, 5], "tenis")
print(round(dawid.average(), 2))
print(dawid)
4.33
Dawid (tenis): 4.33
KLASA BAZOWA Student name, grades average(), __str__() dziedziczy wszystko KLASA POCHODNA Athlete dokłada: sport nadpisuje: __str__()
docs.python.org, The Python Tutorial, rozdz. 9.5 (Inheritance) · docs.python.org, Built-in Functions (super)
Część 4 · Trzy pułapki

Komunikaty, które dziś zobaczycie

Co zrobiłeśCo wypisze PythonPoprawka
W klasie pochodnej brakuje super().__init__(name, grades)AttributeError: 'Athlete' object has no attribute 'name'Pierwsza linia w __init__ klasy pochodnej to wywołanie super()
print(student), a klasa nie ma __str__<__main__.Student object at 0x105e06120>Dopisz __str__ albo wypisuj pojedyncze atrybuty
Metoda wywołana na liście: students.average()AttributeError: 'list' object has no attribute 'average'Metodę ma obiekt, nie lista: students[0].average()

Adres 0x105e06120 u Was będzie inny: to numer miejsca w pamięci, losowany przy każdym uruchomieniu.

Ćwiczenie · przy komputerze · 10 minut

Ćwiczenie: produkty i koszyk

Załóż plik shop.py. Zacznij od klasy Product z lekcji 28: atrybuty name i price oraz metoda price_with_tax, która oddaje cenę razy 1.23 zaokrągloną do dwóch miejsc.

  1. Utwórz dwa produkty: Zeszyt za 12 i Plecak za 180. Wypisz nazwę i cenę z podatkiem każdego z nich.
  2. Napisz klasę Cart: w __init__ pusta lista products, metoda add dokłada produkt, metoda total sumuje ceny z podatkiem. Dodaj oba produkty i wypisz sumę.
  3. Dla chętnych: dopisz class DiscountedProduct(Product) z atrybutem discount w procentach i własną wersją price_with_tax. Dodaj Linijkę za 4 z rabatem 25 do koszyka.
Wskazówka

Metodę __str__ w klasie Cart możesz napisać tak, żeby print(cart) dawało Koszyk: 2 produkty za 236.16. Gdy suma wychodzi dziwnie długa, brakuje round(result, 2) w total.

Sprawdzenie

Trzy pytania na koniec

  1. Dlaczego w pętli piszemy student.average(), a nie students.average()?
  2. Co daje klasie metoda __str__?
  3. Co klasa pochodna dostaje od klasy bazowej i po co woła super()?
Podsumowanie

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
Lista obiektów w programieSklep trzyma produkty w bazie danych, a program wczytuje je do listy obiektów i dalej robi to samocodziennie
Metoda __str__W zapisie zdarzeń każdy obiekt wypisuje się jednym krótkim wierszem, żeby dało się czytać dziennik programucodziennie
Atrybut _discount z podkreślnikiemPodkreślnik na początku nazwy to umowa: „to wnętrze klasy, nie ruszaj z zewnątrz”, choć Python niczego nie blokujestandard w firmach
Dziedziczenie jednej klasyGotowe biblioteki dają klasę bazową, a firma dopisuje własną pochodną z dwiema, trzema metodamistandard w firmach
docs.python.org, The Python Tutorial, rozdz. 9.6 (Private Variables) · peps.python.org/pep-0008/ (Naming Conventions)