12. Klasy i obiekty
Klasa jest formą, na podstawie której tworzone są obiekty. Mówi się, że obiekt jest instancją klasy.

Uwaga: plik [shared] został umieszczony w folderze [Sources Root] projektu.
12.1. Skrypt [classes_01]: klasa Objet
Skrypt [classes_01] przedstawia przestarzały sposób wykorzystania klas:
# klasa
class Objet(object):
"""une classe Objet vide"""
# każda zmienna typu [Objet] może z założenia posiadać atrybuty
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
# wyświetla obiekt
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
# modyfikuje obiekt
obj1.attr2 += 100
# wyświetla obiekt
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
# przypisuje odwołanie obj1 do obj2
obj2 = obj1
# modyfikuje obiekt, na który wskazuje obj2
obj2.attr2 = 0
# wyświetla oba obiekty – obj1 wskazuje teraz na zmodyfikowany obiekt
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
# obj1 i obj2 wskazują ten sam obiekt
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
# typ instancji obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
# w języku Python każdy typ jest obiektem
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")
Uwagi:
- wiersze 2–3: pusta klasa [Objet];
- wiersz 2: klasę można zadeklarować w trzech formach:
- class Obiekt;
- class Obiekt();
- class Obiekt(object);
- wiersz 3: inna forma komentarza. Komentarz poprzedzony trzema znakami „ może rozciągać się na kilka wierszy;
- wiersz 7: instancjonowanie klasy Objet. Wynikiem jest adres, jak widać w wierszach 24–26;
- wiersze 8–9: bezpośrednia inicjalizacja dwóch atrybutów obiektu;
- wiersz 17: kopiowanie odwołań. Zmienne obj1 i obj2 to dwa wskaźniki (odwołania) do tego samego obiektu;
- wiersz 19: modyfikujemy obiekt, na który wskazuje [obj2]. Ponieważ [obj1] i [obj2] wskazują na ten sam obiekt, wyświetlenie obiektu [obj1, obj2] w wierszach 21 i 22 pokaże, że obiekt, na który wskazuje [obj1], uległ zmianie;
- wiersze 24–26: wiersze te mają na celu wykazanie równości zmiennych [obj1] i [obj2]. Wynik wyświetlony w wierszu 26 to potwierdzi. W tym porównaniu równe są adresy [obj1] i [obj2];
- Każdy obiekt w języku Python jest identyfikowany za pomocą unikalnego numeru, który uzyskuje się za pomocą wyrażenia [id(objet)]. Wiersze 24 i 25 pokazują, że numery obiektów, na które wskazują [obj1] i [obj2], są identyczne, co oznacza, że te dwa odwołania wskazują na ten sam obiekt;
- wiersze 27–29: funkcja [isinstance(expr,Type)] zwraca wartość logiczną True, jeśli wyrażenie [expr] jest typu [Type]. W tym przypadku zobaczymy, że [obj1] jest typu [Objet], co wydaje się naturalne, ale jest również typu [object]. Klasa [object] jest klasą nadrzędną wszystkich klas w języku Python. Zgodnie z zasadą dziedziczenia klas klasa potomna F posiada wszystkie właściwości swojej klasy nadrzędnej P, a funkcja [isinstance(instance de F, P)] zwraca wartość True;
- wiersze 30–32: pokazują, że typ [int] jest również typem [object]. Wszystkie typy w języku Python wywodzą się z klasy [object];
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
Process finished with exit code 0
12.2. Skrypt [classes_02]: klasa Personne
Skrypt [classes_02] pokazuje, że atrybuty klasy są publiczne: są dostępne bezpośrednio spoza klasy. Jest to kolejny przykład niezalecanego wykorzystania klas. Przedstawiamy go jednak, ponieważ czasami można spotkać tego typu kod (Python go akceptuje) i należy umieć go zrozumieć.
# klasa Personne
class Personne:
# atrybuty klasy
# niezadeklarowane – mogą być tworzone dynamicznie
# metoda
def identité(self: object) -> str:
# z założenia wykorzystuje nieistniejące atrybuty
return f"[{self.prénom},{self.nom},{self.âge}]"
# ---------------------------------- main
# atrybuty są publiczne i mogą być tworzone dynamicznie
# instancjonowanie klasy
p = Personne()
# bezpośrednia inicjalizacja atrybutów klasy
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
# wywołanie metody klasy
print(f"personne={p.identité()}")
# typ instancji p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")
Uwagi:
- wiersze 2–9: klasa z jedną metodą;
- wiersz 7: każda metoda klasy musi mieć jako pierwszy parametr obiekt self, który oznacza bieżący obiekt. Metoda [identité] zwraca ciąg znaków;
- wiersz 15: instancja obiektu [Personne];
- wiersze 16–19: pokazują, że atrybuty obiektu mogą być tworzone dynamicznie (nie istnieją one w definicji klasy);
- wiersz 9: atrybuty klasy są oznaczone notacją [self.attribut];
- wiersze 23–24 pokazują, że obiekt [p] jest jednocześnie instancją klasy [Personne] oraz klasy [object];
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
Process finished with exit code 0
12.3. Skrypt [classes_03]: klasa Personne z konstruktorem
Skrypt [classes_03] pokazuje typowe zastosowanie klasy:
# klasa Osoba
class Personne:
# konstruktor – inicjuje trzy atrybuty
def __init__(self: object, prénom: str, nom: str, âge: int):
# imię: imię osoby
# nazwisko: nazwisko osoby
# wiek: wiek osoby
self.prénom = prénom
self.nom = nom
self.âge = âge
# zwraca atrybuty klasy w postaci ciągu znaków
def identité(self: object) -> str:
return f"[{self.prénom},{self.nom},{self.âge}]"
# ---------------------------------- ręka
# obiekt typu Osoba
p = Personne("Paul", "de la Hûche", 48)
# wywołanie metody
print(f"personne={p.identité()}")
Uwagi:
- wiersz 4: konstruktor klasy nosi nazwę __init__. Podobnie jak w przypadku innych metod, jego pierwszym parametrem jest self;
- wiersz 20: obiekt Personne jest tworzony za pomocą konstruktora klasy;
- wiersze 13–15: metoda [identité] zwraca ciąg znaków reprezentujący zawartość obiektu;
- wiersz 22: wyświetlenie tożsamości osoby;
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
Process finished with exit code 0
12.4. Skrypt [classes_04]: metody statyczne
W folderze [modules] definiujemy następującą klasę [Utils] (utils.py):

class Utils:
# metoda statyczna
@staticmethod
def is_string_ok(string: str) -> bool:
# czy „string” jest ciągiem znaków
erreur = not isinstance(string, str)
if not erreur:
# czy ciąg znaków jest pusty?
erreur = string.strip() == ''
# wynik
return not erreur
Uwagi
- wiersz 3: adnotacja [@staticmethod] wskazuje, że metoda opatrzona tą adnotacją jest metodą klasy, a nie metodą instancji. Widać to po tym, że pierwszym parametrem tej metody nie jest słowo kluczowe [self]. W związku z tym metoda statyczna nie ma dostępu do atrybutów obiektu. Zamiast pisać:
piszemy
Ponieważ powyżej wpisaliśmy [Utils.is_string_ok], metoda [is_string_ok] jest określana jako metoda klasy (w tym przypadku klasy Utils). W związku z tym metoda [Utils.is_string_ok] musi być opatrzona słowem kluczowym [@staticmethod].
Metoda statyczna [Utils.is_string_ok] pozwala w tym przypadku sprawdzić, czy dana wartość jest niepustym ciągiem znaków.
Skrypt [classes_04] wykorzystuje klasę [Utils] w następujący sposób:
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można wykonać importy
from utilitaires import Utils
print(Utils.is_string_ok(" "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
- wiersze 1–4: stosuje się skrypt konfiguracyjny;
Skrypt konfiguracyjny [config.py] ma następującą postać:
def configure():
import os
# katalog pliku konfiguracyjnego
script_dir = os.path.dirname(os.path.abspath(__file__))
# absolutne ścieżki do folderów, które należy umieścić w syspath
absolute_dependencies = [
f"{script_dir}/shared",
"C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages",
"C:/myprograms/Python38/lib",
"C:/myprograms/Python38/DLLs"
]
# aktualizacja ścieżki systemowej
from myutils import set_syspath
set_syspath(absolute_dependencies)
# zwracamy konfigurację
return {}
- wiersz 9: folder [shared] zostanie umieszczony w ścieżce Python Path;
Wynik wykonania jest następujący:
12.5. Skrypt [classes_05]: sprawdzanie poprawności atrybutów
Skrypt [classes_05] wprowadza nowe pojęcia:
- definicję własnego typu wyjątku;
- definicja metody [_str_], która jest domyślną metodą tożsamości klas;
- definicja właściwości;
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka syspath jest skonfigurowana – można przeprowadzić importy
from utilitaires import Utils
# własna klasa wyjątków wywodząca się z [BaseException]
class MyException(BaseException):
# nie wykonuje się żadnych czynności: pusta klasa
pass
# klasa Personne
class Personne:
# konstruktor
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
# imię: imię osoby
# nazwisko: nazwisko osoby
# wiek: wiek osoby
# zapisanie parametrów
# inicjalizacja zostanie przeprowadzona za pomocą metod setterów
self.prénom = prénom
self.nom = nom
self.âge = âge
# metoda toString klasy
def __str__(self: object) -> str:
return f"[{self.__prénom},{self.__nom},{self.__âge}]"
# metody pobierające
@property
def prénom(self) -> str:
return self.__prénom
@property
def nom(self) -> str:
return self.__nom
@property
def âge(self) -> int:
return self.__âge
# metody ustawiające
@prénom.setter
def prénom(self, prénom: str):
# imię nie może być puste
if Utils.is_string_ok(prénom):
self.__prénom = prénom.strip()
else:
raise MyException("Le prénom doit être une chaîne de caractères non vide")
@nom.setter
def nom(self, nom: str):
# imię nie może być puste
if Utils.is_string_ok(nom):
self.__nom = nom.strip()
else:
raise MyException("Le nom doit être une chaîne de caractères non vide")
@âge.setter
def âge(self, âge: int):
# wiek musi być liczbą całkowitą >=0
erreur = False
if isinstance(âge, int):
if âge >= 0:
self.__âge = âge
else:
erreur = True
else:
erreur = True
# błąd?
if erreur:
raise MyException("L'âge doit être un entier >=0")
# ---------------------------------- main
# obiekt typu Osoba
try:
# utworzenie instancji klasy Osoba
p = Personne("Paul", "de la Hûche", 48)
# wyświetlenie obiektu p
print(f"personne={p}")
except MyException as erreur:
# wyświetlenie błędu
print(erreur)
# kolejny obiekt typu Osoba
try:
# instancja klasy Osoba
p = Personne("xx", "yy", "zz")
# wyświetlanie obiektu p
print(f"personne={p}")
except MyException as erreur:
# wyświetlanie błędu
print(erreur)
# kolejna osoba, tym razem bez parametrów
try:
# utworzenie instancji klasy Osoba
p = Personne()
# wyświetlenie obiektu p
print(f"personne={p}")
except MyException as erreur:
# wyświetlanie komunikatu o błędzie
print(erreur)
# nie można uzyskać dostępu do atrybutów prywatnych klasy __attr
p.__prénom = "Gaëlle"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
p.prénom = "Sébastien"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
Uwagi:
- wiersze 10–13: klasa MyException wywodząca się z klasy BaseException (omówimy to nieco później). Nie dodaje ona żadnych funkcji do tej ostatniej. Znajduje się tam wyłącznie po to, aby posiadać własny wyjątek;
- wiersz 19: konstruktor ma domyślne wartości dla swoich parametrów. W związku z tym operacja p=Personne() jest równoważna operacji p=Personne("x","y",0);
- wiersze 34–45: właściwości klasy. Są to metody opatrzone słowem kluczowym [@property]. Służą one do zwracania wartości atrybutów;
- wiersze 47–77: metody ustawiające klasy. Są to metody opatrzone słowem kluczowym [@attributsetter]. Służą one do ustalania wartości atrybutów;
- wiersze 48–54: metoda ustawiająca atrybutu [prénom]. Metoda ta będzie wywoływana za każdym razem, gdy przypisywana będzie wartość do atrybutu [prénom]:
Wiersz 2 spowoduje wywołanie metody [p.prénom(valeur)]. Zaletą korzystania z metody setter do przypisania wartości do atrybutu jest to, że jako że metoda ta jest funkcją, można sprawdzić poprawność wartości przypisanej do atrybutu;
- wiersz 51: sprawdzamy, czy wartość przypisana do atrybutu [prénom] jest niepustym ciągiem znaków. W tym celu wykorzystujemy statyczną metodę [Utils.isStringOk], którą omówiliśmy wcześniej;
- wiersz 52: wartość przypisana do atrybutu [prénom] jest pozbawiana „spacji” na początku i na końcu ciągu znaków, a następnie przypisywana do atrybutu [self.__prénom]. Nie jest więc tutaj używany atrybut [prénom]. Nie można było tego zrobić, ponieważ w przeciwnym razie doszłoby do nieskończonego wywołania rekurencyjnego. Można było użyć dowolnej nazwy atrybutu. Zastosowanie atrybutu [__prénom] z dwoma znakami podkreślenia na początku identyfikatora ma szczególne znaczenie: atrybuty poprzedzone dwoma znakami podkreślenia są prywatne dla klasy. Oznacza to, że nie są one widoczne spoza tej klasy. Nie można zatem napisać:
W rzeczywistości wkrótce zobaczymy, że można to zapisać, ale nie zmienia to imienia. Ma to inne skutki;
- wiersze 53–54: jeśli wartość przypisana do imienia jest nieprawidłowa, zgłaszany jest wyjątek. Dzięki temu kod wywołujący będzie wiedział, że wywołanie jest nieprawidłowe;
- wiersze 35–37: właściwość [prénom]. Będzie ona wywoływana za każdym razem, gdy w wyrażeniu pojawi się [p.prénom]. Wówczas wywoływana będzie metoda [p.prénom()]. W wierszu 37 zwracamy wartość atrybutu [__prénom], ponieważ zauważyliśmy, że setter atrybutu [prénom] przypisywał jego wartość do prywatnego atrybutu [__prénom];
- wiersze 56–62: setter atrybutu [nom] jest skonstruowany analogicznie do settera atrybutu [prénom]. To samo dotyczy settera atrybutu [âge] w wierszach 64–77;
- chociaż właściwości [prénom, nom, valeur] nie są prawdziwymi atrybutami, którymi w rzeczywistości są [__prénom, __nom, __âge], będziemy nadal nazywać je atrybutami klasy, ponieważ są one używane jako takie;
- wiersze 19–28: konstruktor klasy domyślnie wykorzystuje metody ustawiające atrybutów [prénom, nom, âge]. W rzeczywistości, wpisując w wierszu 26 [self.prénom = prénom], domyślnie wywoływana jest metoda [prénom(self, prénom)]. Następnie sprawdzana jest poprawność parametru [prénom]. To samo dotyczy dwóch pozostałych atrybutów [nom, âge];
- w tym modelu nie można przypisać nieprawidłowych wartości do atrybutów klasy o nazwie [prénom, nom, âge];
- wiersze 30–32: funkcja __str__ zastępuje metodę, która wcześniej nosiła nazwę identité. Nazwa [__str__] (z dwoma znakami podkreślenia przed i po) nie jest przypadkowa. Zobaczymy to później;
- wiersze 83–86: instancjonowanie osoby, a następnie wyświetlenie jej tożsamości;
- wiersz 84: instancjonowanie;
- wiersz 86: wyświetlenie. Operacja wymaga wyświetlenia osoby o nazwie p w postaci ciągu znaków. Interpreter języka Python automatycznie wywołuje wówczas metodę p.__str__(), o ile istnieje. Metoda ta pełni tę samą rolę, co metoda toString() w Javie lub w językach .NET;
- wiersze 87–89: obsługa ewentualnego wyjątku typu MyException. Następnie wyświetla błąd;
- wiersze 91–99: to samo dotyczy drugiej osoby instancjonowanej z błędnymi parametrami;
- wiersze 102–109: to samo dotyczy trzeciej osoby instancjonowanej z parametrami domyślnymi: nie przekazujemy żadnych parametrów. W tym przypadku wykorzystywane są domyślne wartości tych parametrów w konstruktorze;
- wiersze 112–117: ustalono, że atrybut [__prénom] jest prywatny, a zatem normalnie niedostępny spoza klasy. Chcemy to sprawdzić;
- wiersze 112–114: przypisujemy wartość do atrybutu [__prénom], a następnie sprawdzamy wartości atrybutów [__prénom] i [prénom], które normalnie powinny być takie same;
- wiersze 115–117: powtarzamy operację, tym razem inicjując atrybut [prénom];
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
Process finished with exit code 0
Uwagi
- wiersze 5–6: widać, że przypisanie [p.__prénom = "Gaëlle"] nie zmieniło wartości atrybutu [prénom], wiersz 5;
- wiersze 7–8: widać, że przypisanie [p.prénom = "Sébastien"] nie zmieniło wartości atrybutu [__prénom], wiersz 8;
Co z tego wynika? Prawdopodobnie operacja [p.__prénom = "Gaëlle"] utworzyła w klasie atrybut publiczny [__prénom], ale różni się on od atrybutu prywatnego [__prénom], na którym dokonano operacji w ramach tej klasy;
12.6. Skrypt [classes_06]: dodanie metody inicjalizacji obiektu
Skrypt [classes_06] dodaje metodę do klasy [Personne]:
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można przeprowadzić importy
from utilitaires import Utils
# klasa wyjątkowa typu własnego wywodząca się z [BaseException]
class MyException(BaseException):
# nie wykonuje się żadnych działań: klasa pusta
pass
# klasa Personne
class Personne:
# konstruktor
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
# imię: imię osoby
# nazwisko: nazwisko osoby
# wiek: wiek osoby
# zapisywanie parametrów
# inicjalizacja zostanie przeprowadzona za pomocą metod setterów
self.prénom = prénom
self.nom = nom
self.âge = âge
# inna metoda inicjalizacji
def init_with_personne(self: object, p: object):
# inicjuje bieżący obiekt z osobą p
self.__init__(p.prénom, p.nom, p.âge)
# metoda toString klasy
def __str__(self: object) -> str:
return f"[{self.__prénom},{self.__nom},{self.__âge}]"
# metody pobierające
…
# metody ustawiające
…
# ---------------------------------- main
# obiekt klasy Personne
try:
# utworzenie instancji klasy Osoba
p = Personne("Paul", "de la Hûche", 48)
# wyświetlenie obiektu p
print(f"personne={p}")
except MyException as erreur:
# wyświetlenie komunikatu o błędzie
print(erreur)
# kolejny obiekt Osoba
try:
# utworzenie instancji klasy Osoba
p = Personne("xx", "yy", "zz")
# wyświetlanie obiektu p
print(f"p={p}")
except MyException as erreur:
# wyświetlenie komunikatu o błędzie
print(erreur)
# kolejna osoba, tym razem bez parametrów
try:
# utworzenie instancji klasy Osoba
p = Personne()
# wyświetlanie obiektu p
print(f"p={p}")
except MyException as erreur:
# wyświetlenie komunikatu o błędzie
print(erreur)
# kolejna osoba uzyskana przez kopiowanie
try:
# utworzenie instancji klasy Osoba
p2 = Personne()
p2.init_with_personne(p)
# wyświetlenie obiektu p2
print(f"p2={p2}")
except MyException as erreur:
# wyświetlenie komunikatu o błędzie
print(erreur)
Uwagi:
- różnica w stosunku do poprzedniego skryptu znajduje się w wierszach 30–33. Dodano metodę initWithPersonne. Metoda ta wywołuje konstruktor __init__. Nie ma możliwości, tak jak w językach typowanych, posiadania metod o tej samej nazwie, rozróżnianych na podstawie charakteru ich parametrów lub wyniku. Nie ma zatem możliwości posiadania wielu konstruktorów, które tworzyłyby obiekt na podstawie różnych parametrów, w tym przypadku obiekt typu Personne;
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
Process finished with exit code 0
12.7. Skrypt [classes_07]: lista obiektów typu Personne
Umieszczamy teraz klasy [MyException] i [Personne] w module, aby móc z nich korzystać bez konieczności przepisywania ich kodu:

Obie klasy znajdą się w powyższym module [myclasses.py].
Skrypt [classes_07] pokazuje, że można uzyskać listę obiektów:
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można wykonać importy
from myclasses import Personne
# ---------------------------------- main
# tworzenie listy obiektów typu osoba
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
# tożsamość tych osób
for i in range(len(groupe)):
print(f"groupe[{i}]={groupe[i]}")
Uwagi:
- wiersz 7: importujemy klasę [Personne];
- wiersz 11: lista obiektów typu [Personne];
- wiersze 13–14: przeglądamy tę listę, aby wyświetlić każdy z jej elementów;
- wiersz 14: funkcja [print] wyświetli ciąg znaków reprezentujący obiekt [groupe[i]]. Domyślnie zostanie wywołana metoda [__str__] tego obiektu;
Wyniki
C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.8. Skrypt [classes_08]: utworzenie klasy pochodnej od klasy Personne
W module [myclasses] definiujemy następującą klasę [Enseignant]:
# klasa Nauczyciel
class Enseignant(Personne):
# konstruktor
def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
# imię: imię osoby
# nazwisko: nazwisko osoby
# wiek: wiek osoby
# przedmiot: nauczany przedmiot
# inicjały rodzica
Personne.__init__(self, prénom, nom, âge)
# inne inicjały
self.discipline = discipline
# toString
def __str__(self) -> str:
return f"enseignant[{super().__str__()},{self.discipline}]"
# właściwości
@property
def discipline(self) -> str:
return self.__discipline
@discipline.setter
def discipline(self, discipline: str):
# dyscyplina musi być niepustym ciągiem znaków
if Utils.is_string_ok(discipline):
self.__discipline = discipline
else:
raise MyException("La discipline doit être une chaîne de caractères non vide")
- wiersz 2: deklaruje klasę Enseignant jako klasę pochodną klasy Personne. Klasa pochodna posiada wszystkie właściwości (atrybuty i metody) swojej klasy nadrzędnej oraz własne;
- wiersz 13: klasa [Enseignant] definiuje nowy atrybut [discipline];
- wiersz 11: konstruktor klasy pochodnej Enseignant musi wywołać konstruktor klasy nadrzędnej Personne, przekazując mu oczekiwane parametry;
- wiersz 17: funkcja [super()] zwraca klasę nadrzędną. W tym miejscu wywoływana jest funkcja [__str__] klasy nadrzędnej;
- wiersze 19–30: definiuje się metody getter i setter nowego atrybutu [discipline];
Skrypt [classes_08] wykorzystuje klasę [Enseignant] w następujący sposób:
# konfigurujemy aplikację
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można wykonać importy
from myclasses import Personne, Enseignant
# ---------------------------------- main
# utworzenie tablicy obiektów typu Personne i pochodnych
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
# tożsamość tych osób
for i in range(len(groupe)):
print(f"groupe[{i}]={groupe[i]}")
Uwagi:
- wiersz 7: importuje się klasy [Personne] i [Enseignant] zdefiniowane w pliku [myclasses.py];
- wiersze 11–14: definiuje się grupę osób, a następnie wyświetla się ich tożsamość;
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.9. Skrypt [classes_09]: druga klasa wywodząca się z klasy Osoba
Skrypt [classes_09] wprowadza klasę [Etudiant], która wywodzi się z klasy [Personne]. Klasa ta jest zdefiniowana w następujący sposób w module [myclasses]:
# klasa Student
class Etudiant(Personne):
# konstruktor
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
Personne.__init__(self, prénom, nom, âge)
self.formation = formation
# toString
def __str__(self: object) -> str:
return f"étudiant[{super().__str__()},{self.formation}]"
# właściwości
@property
def formation(self) -> str:
return self.__formation
@formation.setter
def formation(self, formation: str):
# szkolenie musi być niepustym ciągiem znaków
if Utils.is_string_ok(formation):
self.__formation = formation
else:
raise MyException("La formation doit être une chaîne de caractères non vide")
Skrypt [classes_09] wykorzystuje klasę [Etudiant] w następujący sposób:
# konfigurujemy aplikację
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można przeprowadzić importy
from myclasses import Personne, Enseignant, Etudiant
# ---------------------------------- main
# tworzenie tablicy obiektów typu Personne i pochodnych
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
Etudiant("Steve", "Boer", 22, "iup2 qualité")]
# tożsamość tych osób
for personne in groupe:
# wyświetlanie osoby
print(personne)
Uwagi:
- skrypt ten jest analogiczny do poprzedniego.
Wyniki
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
Process finished with exit code 0
12.10. Skrypt [classes_10]: właściwość [__dict__]
Skrypt [classes_10] zawiera właściwość [__dict__], z której będziemy często korzystać w dalszej części:
# konfiguracja aplikacji
import config
config = config.configure()
# ścieżka systemowa jest skonfigurowana – można przeprowadzić importy
from myclasses import Etudiant
# ---------------------------------- main
# utworzenie studenta
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
# słownik właściwości
print(étudiant.__dict__)
Komentarze
- wiersze 1–4: konfiguracja aplikacji;
- wiersz 7: importowana jest klasa [Etudiant];
- wiersz 11: utworzenie instancji studenta;
- wiersz 13: użycie predefiniowanej metody [__dict__] (2 znaki podkreślone przed i po identyfikatorze);
Wyniki są następujące:
- w wierszu 2 otrzymujemy słownik, którego klucze to właściwości obiektu poprzedzone prefiksem nazwy klasy, do której należą. Wykorzystamy ten słownik do utworzenia pomostu między obiektem a słownikiem;