Zacznijmy od prostej instrukcji print()
. Za jej pomocą możemy kazać komputerowi wyświetlić coś na ekranie. To „coś” umieszczamy jako argument tej instrukcji między nawiasami.
print('coś')
coś
Zauważ, że napis „coś” w powyższym kodzie jest otoczony z obu stron amerykańskim cudzysłowem pojedynczym '
. To informacja dla komputera, że „coś” jest niczym więcej niż zwykłym napisem (ang. string). Właśnie poznaliśmy pierwszy typ danych „napis”.
print()
- wyświetla informację na ekranie.
Instrukcje Pythona takie jak print()
będziemy nazywać funkcjami. Więcej o funkcjach powiemy nieco później.
Teraz zastanówmy się, co by się stało, gdybyśmy przekazali komputerowi następującą instrukcję:
print(coś)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Input In [2], in <cell line: 1>() ----> 1 print(coś) NameError: name 'coś' is not defined
Python odpowie nam komunikatem typu NameError
, o treści „zmienna (ang. variable) o nazwie coś nie została zdefiniowana”.
Co to jest zmienna? Na to pytanie jest tylko jedna odpowiedź: zmienna to nie jest stała. To paradoksalnie tautologiczne ujęcie rzeczy mówi wszystko o tym czym jest zmienna. Stałą jest na przykład poznany wcześniej napis 'coś'. Jest on taki jaki jest i nie może być inny. Zmienna natomiast zmienną jest, czyli może przyjmować różną postać, tj. różne wartości. Weźmy na przykład zmienną current_year
i przypiszmy jej wartość 2021
, co zapisujemy w Pythonie:
current_year = 2021
print(current_year)
2021
Rok później przypiszemy tej zmiennej inną wartość:
current_year = 2022
print(current_year)
2022
I tak co roku wartość ta będzie się zwiększać o 1. Instrukcja print(current_year)
wyświetli co roku inną wartość, najpierw 2021, potem 2022, itd.
Zauważmy, w jaki sposób przypisujemy wartości do zmiennej. Instrukcja przypisania (ang. assignment statement), składa się z nazwy zmiennej (current_year
), znaku równości (=
), zwanego operatorem przypisania (ang. assignment operator) oraz wartości, która ma zostać przypisana (2021
). Tak na marginesie, właśnie poznaliśmy drugi typ danych: liczby całkowite (ang. integer). Zmienna current_year
jest właśnie tego typu.
Programując będziemy używać języka angielskiego do nazw zmiennych (a później również funkcji, metod i klas). Język polski może pojawić się w komunikatach zwracanych do użytkownika.
Wiemy już czym są napisy i jak je wyświetlić. Na przykład instrukcja poniżej wyświetli napis „Witaj świecie!”:
print('Witaj świecie!')
Witaj świecie!
Napisy można ze sobą łączyć za pomocą +
. Poniższa instrukcja wyświetli napis „Ala ma kota.” (zwróć uwagę na spacje przed i po „ma”):
print('Ala' + ' ma ' + 'kota.')
Ala ma kota.
Podobny efekt uzyskamy pisząc:
print('Ala', 'ma', 'kota.')
Ala ma kota.
Sprawdź samodzielnie jaki będzie wynik wykonania poniższej instrukcji:
print('Ala', 'ma', 'kota', sep='-', end='.')
Ala-ma-kota.
Poniższe dwie linie kodu pokazują, że znak +
zachowuje się inaczej gdy jest zastosowany do napisów, a inaczej, gdy stosujemy go do liczb. Zauważ różnice.
print('10' + '5')
105
print(10 + 5)
15
Pokażemy teraz jak można wstawić coś wewnątrz napisu. Przypomnijmy, że wcześniej zdefiniowaliśmy zmienną:
current_year = 2022
Dodamy teraz jeszcze jedną:
my_age = current_year - 1979
print(my_age)
43
Użyjmy ich teraz do w następujących pięciu wywołaniach funkcji print()
:
print('Mam', my_age, 'lat(a).')
print('Mam %d lat(a).' % my_age)
print('Mam %s lat(a).' % my_age)
print('Mam {} lat(a).'.format(my_age))
print(f'Mam {my_age} lat(a).')
Mam 43 lat(a). Mam 43 lat(a). Mam 43 lat(a). Mam 43 lat(a). Mam 43 lat(a).
Przeanalizuj również poniższy kod.
hello = 'Cześć %s!'
print(hello % 'Maciek')
Cześć Maciek!
Kod Pythona można opatrywać komentarzami. Jest to bardzo porządane, gdyż ułatwia zrozumienie kodu, szczególnie, gdy do niego wracamy po dłuższym czasie niewidzenia. Python umożliwa dwa typy komentarzy:
# komentarz w jednej linii
print('Nie zobaczysz komentarzy!') # komentarz w jednej linii
Nie zobaczysz komentarzy!
"""
komentarz
w kilku
liniach
"""
print('Nie zobaczysz komentarzy!')
Nie zobaczysz komentarzy!
Wewnątrz napisu można umieszczać znaki specjalne (ang. escape characters). Znaki specjalne zawsze zaczynają się od backslasha „\
”. Oto przykłady działania znaku tabulacji „\t
” i nowej linii „\n
”:
print('\tPink Floyd') # tabulacja
Pink Floyd
print('Pink\nFloyd') # nowa linia
Pink Floyd
Do znaków specjalnych wrócimy jeszcze później, gdy będziemy mówić o wyrażeniach regularnych.
Teraz poznamy drugą funkcję len()
. Jej argumentem, czyli tym co umieścimy między nawiasami „(
” i „)
”, może być napis, a tym co ona zwraca jest jego długość. Na przykład:
len('Ummagumma') # liczba znaków
9
len('Ummagumma ')
15
len(' Ummagumma ')
17
Widać więc, że spacje są również liczone gdy sprawdzamy długość napisów.
Python posiada instrukcje do usuwanie spacji z początku lub końca napisu.
print(' Ummagumma '.rstrip()) # usuwanie wszytkich spacji z prawej strony napisu
print(' Ummagumma '.lstrip()) # usuwanie wszytkich spacji z lewej strony napisu
print(' Ummagumma '.strip()) # usuwanie wszytkich spacji z obu stron napisu
Ummagumma Ummagumma Ummagumma
print(len(' Ummagumma '.rstrip()))
print(len(' Ummagumma '.lstrip()))
print(len(' Ummagumma '.strip()))
10 10 9
Takie instrukcje, które „doczepiamy” za pomocą kropki .
(na przykład do napisów, po to, aby je przekształcić) nazywamy metodami. To, do czego „doczepiamy” metodę nazywa się obiektem. Każdy napis jest obiektem (choć nie każdy obiekt jest napisem, co znaczy, że poznamy jeszcze inne obiekty, które nie będą napisami).
Mówimy, że metoda jest wywoływana na obiekcie (gdy ją do niego „doczepiamy”). W poniższym przykładzie napis ' Ummagumma '
jest obiektem, na którym wywołujemy metodę strip()
.
' Ummagumma '.strip()
'Ummagumma'
Oto inne pożyteczne metody, jakie można wywołać na napisach:
print('robert trypuz'.title())
print('RoBert TrypUz'.title())
print('robert trypuz'.upper())
print('RoBert TrypUz'.lower())
print('ala ma kota.'.capitalize())
Robert Trypuz Robert Trypuz ROBERT TRYPUZ robert trypuz Ala ma kota.
Jeśli chcesz znaleźć fragment napisu w napisie użyj metod find()
lub index()
. Każdy znak w napisie ma swój numer porządkowy. Zaczynami numerować znaki od 0. Czyli w napisie „kot ”, znak „k” będzie miał indeks 0, „o” indeks 1, „t” indeks 2 i „ ” indeks 3. Metody find()
i index()
zwrócą indeks pierwszego wystąpienia szukanego fragmentu. Przeanalizuj samodzielnie poniższy przykład.
yoda_sentence = 'Dwóch zawsze ich jest. Nie mniej, nie więcej. Mistrz i uczeń.'
print(yoda_sentence.find('uczeń'))
print(yoda_sentence.index('uczeń'))
print(yoda_sentence.find('Yoda'))
55 55 -1
W przypadku, gdy szukanego fragment nie będzie w napisie, metoda find()
zwróci -1, zaś metoda index()
zwróci wyjątek („substring not found”).
print(yoda_sentence.index('Yoda'))
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Input In [24], in <cell line: 1>() ----> 1 print(yoda_sentence.index('Yoda')) ValueError: substring not found
Wróćmy jeszcze do tego, że każdy znak w napisie ma swój numer. Za pomocą indeksów możemy „wykroić” z napisu jeden znak albo więcej znaków. Przeanalizuj wyniki poniższego kodu:
# slicing
print('Robert Trypuz'[3])
print('Robert Trypuz'[3:10])
print('Robert Trypuz'[3:10:2])
e ert Try etTy
t = 'Thor, syn Odyna, odpowiednik południowogermańskiego Donara, jeden z głównych bogów nordyckich'
m = t[19]+t[12]+t[0:3]+t[8]+t[5]+t[60]+t[23]+t[6]+t[0]+t[5]+t[6]+t[32]+t[19]+t[23]+t[3]
print(m.capitalize(), end='!')
Python jest super!
Metoda count()
zwraca liczbę wystąpień fragmentu napisu (znaku, wyrazu lub frazy) w napisie:
print(yoda_sentence.count('uczeń'))
print(yoda_sentence.count('nie'))
1 2
replace()
domyślnie zastępuje wszystkie wystąpienia pierwszego argumentu przez drugi.
print(yoda_sentence.replace('uczeń','padawan'))
Dwóch zawsze ich jest. Nie mniej, nie więcej. Mistrz i padawan.
Napiszmy teraz krótki programik, który wykorzysta zdobytą przez nas do tej pory wiedzę. W programie wykorzystamy nową funkcję input()
- to już trzecia funkcja jaką poznajesz! Odczytuje ona to co napisze użytkownik i przekształca to na napis.
Po wykonaniu poniższych czterech linii kodu, komputer poprosi cię o podanie twojego imienia, a następnie przywita się z tobą i poda z ilu liter składa się twoje imię:
print('Podaj swoje imię: ')
name = input()
print(f'Witaj {name.title()}!')
print(f'Twoje imię ma {len(name)} znaków.')
Podaj swoje imię: Robert Witaj Robert! Twoje imię ma 6 znaków.
Wróćmy jeszcze na chwilę do zmiennych i podsumujmy co o nich wiemy. Używanie zmiennych ma wiele zalet. Zmienna umożliwia odnoszenie się do wartości za pomocą nazw oraz do raz wprowadzonej zmiennej można odnosić się kilkakrotnie:
current_year = 2022
my_age = current_year - 1979
print('Mam', my_age, 'lat(a).')
Mam 43 lat(a).
Używanie zmiennej pozwala na łatwiejsze zrozumienie znaczenia kodu programu (co ma ogromne znaczenie w przypadku dużych projektów nad którymi pracuje kilku programistów). Pamiętaj również, że nazwy zmiennych muszą spełniać określone zasady:
_
(-
nie jest dozwolony!).capitalize()
- metoda zwraca kopię oryginalnego napisu i konwertuje pierwszy jego znak na wielką literę, a pozostałe litery zamienia na małe.
count()
- metoda zwraca liczbę wystąpień fragmentu napisu w napisie.
find()
- metoda zwraca najniższy indeks fragmentu napisu w danym napisie, o ile zostanie znaleziony. Jeśli nie zostanie znaleziony, zwraca -1.
index()
- metoda zwraca najniższy indeks fragmentu napisu w danym napisie, o ile zostanie znaleziony. Jeśli nie zostanie znaleziony, zwraca błąd.
input()
- funkcja pobiera dane wejściowe od użytkownika i zwraca je.
len()
- funkcja zwraca liczbę znaków w napisie.
lower()
- metoda zamienia wszystkie wielkie litery w napisie na małe.
lstrip()
- metoda zwraca kopię napisu z usuniętymi spacjami z początku napisu.
replace()
- metoda zwraca kopię napisu, w którym wszystkie wystąpienia fragmentu tego napisu są zastępowane innym napisem.
rstrip()
- metoda zwraca kopię napisu z usuniętymi spacjami z końca napisu.
strip()
- metoda zwraca kopię napisu z usuniętymi spacjami z początku i końca napisu.
title()
- metoda zamienia pierwszy znak w napisie na wielką literę, a pozostałe znaki zamienia na małe litery.
upper()
- metoda zamienia wszystkie małe litery w napisie na wielkie.
Oprócz napisów mamy również w Pythonie liczby. Możemy w Pythonie wykonywać arytmetyczne operacje na liczbach. Uruchamiając poniższe przykłady bez trudu zrozumiesz sens tych operacji:
print("6 + 3 =", 6 + 3)
print("6 - 3 =", 6 - 3)
print("6 * 3 =", 6 * 3)
print("6 / 3 =", 6 / 3)
print("6 % 3 =", 6 % 3)
print("6 ** 3 =", 6 ** 3)
print("7 // 3 =", 7 // 3)
6 + 3 = 9 6 - 3 = 3 6 * 3 = 18 6 / 3 = 2.0 6 % 3 = 0 6 ** 3 = 216 7 // 3 = 2
Wyżej mówiliśmy o przypisaniu wartości do stworzonej zmiennej.
x = 3
print(x)
3
Kod w komórce powyżej przypisuje wartość 3 zmiennej x
. Gdybyśmy teraz chcieli zwiększyć wartość x
o jeden (niezależnie od tego ile ona teraz wynosi) moglibyśmy zrobić tak:
x = x + 1
print(x)
4
W Pythonie możemy łączyć operacja przypisania wartości do zmiennej z wykonywaniem operacji arytmetycznych. Na przykład poniża instrukcja zrobi dokładnie to samo co x = x + 1
:
x += 1
print(x)
5
Łączymy +
z =
i mamy +=
, czyli jednoczesne przypisanie z sumą. Podobnie możemy zrobić z innymi operacjami arytmetycznymi. Na przykład:
x = 3
x *= 2
print(x)
6
Powiedzieliśmy na początku, że programowanie, to wydawanie instrukcji komputerowi: zrób to, a potem tamto, itd. Teraz powiemy, że instrukcje mogą być nieco bardziej złożone i niekiedy warunkowe, tj. ich wykonanie będzie zależeć od spełnienia (tj. prawdziwości) zadanego przez nas warunku.
Możemy zapytać komputer używając Pythona, czy dane wyrażenie jest prawdziwe (True
) czy fałszywe (False
).
2+2 == 4
True
2+2 != 4
False
16 > 18
False
19 >= 18
True
Użyte w czterech powyższych komórkach operatory są dość oczywiste, ale powiedzmy dla jasności, że ==
,!=
, >
, >=
znaczą odpowiednio „jest identyczne”, „jest różne”, „jest większe” oraz „jest większe bądź równe”.
W Pythonie mamy wiele metod, które będą zwracać wartości True
lub False
. Na przykład: isupper()
, islower()
, isalpha()
, isdigit()
, czy endswith()
. Jeśli uruchomisz każde z poniższych linii kodu w osobnej komórce notatnika w tym porządku w jakim są poniżej, to otrzymasz jako wynik na zmianę True
i False
.
'ROBERT TRYPUZ'.isupper()
True
'KUL'.islower()
False
'Robert'.isalpha()
True
'Robert Trypuz'.isalpha()
False
'123'.isdigit()
True
'10 000'.isdigit()
False
'Robert Trypuz'.endswith('Trypuz')
True
Zauważ, że przedostatnie wyrażenie zwróci wartość False
. Dzieje się tak dlatego, że w napisie 10 000
jest spacja. Spacja jest też powodem, dla którego instrukcja 'Robert Trypuz'.isalpha()
zwróci wartość False
.
endswith()
- metoda zwraca True
, jeśli napis kończy się określonym sufiksem. Jeśli nie, zwraca False
.
isalpha()
- metoda zwraca True
, jeśli wszystkie znaki w napisie są literami z alfabetu („alpha” to skrót od „alphabet”). Jeśli nie, zwraca False
.
isdigit()
- metoda zwraca True
, jeśli wszystkie znaki w napisie są cyframi. Jeśli nie, zwraca False
.
islower()
- metoda zwraca True
, jeśli wszystkie litery w napisie są małymi literami. Jeśli napis zawiera co najmniej jedną wielką literę, zwraca False
.
isupper()
- metoda zwraca True
, jeśli wszystkie litery w napisie są dużymi literami. Jeśli napis zawiera co najmniej jedną małą literę, zwraca False
.
isnumeric()
- metoda zwraca True
, jeśli wszystkie znaki w napisie są znakami liczbowymi. Jeśli nie, zwraca False
.
isspace()
- metoda zwraca True
, jeśli w napisie znajdują się tylko białe znaki (tj. Nie ma znaków specjalnych jak \n
czy \t
). Jeśli nie, zwraca False
.
startswith()
- metoda zwraca True
, jeśli napis kończy się określonym prefiksem. Jeśli nie, zwraca False
.
Instrukcja warunkowa to instrukcja, którą komputer wykona pod pewnym warunkiem.
if WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
Poniższy kod każe komputerowi wyświetlić napis
Jesteś pełnoletni(a) i możesz głosować!
Brawo!
Masz 19 lat(a).
pod warunkiem, że wartość przypisana zmiennej my_age
będzie >= 18
.
my_age = 19
if my_age >= 18:
print('Jesteś pełnoletni(a) i możesz głosować!')
print('Brawo!')
print(f'Masz {my_age} lat(a).')
Jesteś pełnoletni(a) i możesz głosować! Brawo! Masz 19 lat(a).
Teraz zmieńmy wartość zmiennej my_age
i zobaczmy różnicę w działaniu kodu:
my_age = 16
if my_age >= 18:
print('Jesteś pełnoletni(a) i możesz głosować!')
print('Brawo!')
print(f'Masz {my_age} lat(a).')
Masz 16 lat(a).
Zauważmy, że instrukcje
print('Jesteś pełnoletni(a) i możesz głosować!')
print('Brawo!')
w powyższym kodzie zaczynają się „wcięciem” (tabulacją), podczas gdy reszta kodu nie. To nie jest tylko zabieg estetyczny. W ten sposób tworzymy w Pythonie bloki kodu. W tym przypadku chcemy zaznaczyć, że obie instrukcje print()
zaczynające się tabulacją należą do tego samego bloku kodu, który wykona się o ile spełniony będzie warunek.
Poniższy kod wykorzystuje wcześniej poznaną metodę count()
oraz instrukcję warunkową do wykrywania wulgaryzmów w tekście.
entry = 'Nie zgadzam się z tym stanem rzeczy, kurza twarz!'
vulgarism = 'kurza twarz'
if entry.count(vulgarism) > 0:
print('Komentarz zawiera wyrażenie uznane za obraźliwe.')
Komentarz zawiera wyrażenie uznane za obraźliwe.
Instrukcja warunkowa może być nieco bardzie złożona i przybrać postać instrukcji if-else
:
if WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
else:
BLOK INSTRUKCJI DO WYKONANIA
Blok instrukcji następujący po else
wykona się o ile warunek następujący po if
nie będzie spełniony (prawdziwy). Uruchom poniższy kod kilkakrotnie i zrozum jego działanie. Program sprawdza, czy znak wprowadzony przez użytkownika jest samogłoską.
my_age = 14
if my_age >= 18:
print('Jesteś pełnoletni(a) i możesz głosować!')
else:
print(f'Masz {my_age} lat. Nie możesz głosować!')
Masz 14 lat. Nie możesz głosować!
in
w poniższym kodzie to operator przynależności służący do sprawdzenia, czy dany element występuje w sekwencji. W tym kodzie chodzi o sprawdzenie, czy znak jest samogłoską.
Możemy użyć not in
, aby się upewnić, że danego elementu w sekwencji nie ma.
print('Podaj znak: ')
sign = input()
vowels = 'aiueoóyąę'
if sign.lower() in vowels:
print(f'Wprowadzony znak \"{sign}\" jest samogłoską.')
else:
print(f'Wprowadzony znak \"{sign}\" nie jest samogłoską.')
Podaj znak: - Wprowadzony znak "-" nie jest samogłoską.
Instrukcja warunkowa może być jeszcze bardziej złożona jeśli pomiędzy if
i else
dodamy jeszcze elif
. Otrzymamy wówczas następujący schemat if-elif-else
:
if WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
elif WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
…
elif WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
else:
BLOK INSTRUKCJI DO WYKONANIA
Instrukcja if-elif-else
pozwala „zniuansować” warunki. W przypadku if-else
wybór był binarny (albo tak albo inaczej). Fragment elif
pozwala wprowadzić dodatkowe warunki. Uruchom poniższy program i zrozum jego działanie. To program wyborczy informujący o (nie)możliwości głosowania. Zmień wartość zmiennej my_age
, aby uruchomić różne warunki.
my_age = 15
if my_age >= 18:
print('Jesteś pełnoletni(a) i możesz głosować!')
elif my_age >= 17:
print(f'Już za rok będziesz głosować!')
elif my_age >= 16:
print(f'Już za dwa lata będziesz głosować!')
else:
print(f'Masz {my_age} lat... jeszcze poczekasz!')
Masz 15 lat... jeszcze poczekasz!
Program sprawdzający, czy student zaliczył czy też nie ćwiczenia ze wskazaniem powodu ew. niezaliczenia.
missed_classes = 3
passed_tests = 3 # out of 4
class_participation = True
if missed_classes > 2:
print('Brak zaliczenia z powodu nieobecności!')
elif passed_tests <= 2:
print('Brak zaliczenia; nie zaliczyłeś/aś min 3 testów!')
elif not class_participation:
print("Brak zaliczenia z powodu braku aktywności!")
else:
print('Zaliczone!', 'Gratulacje!')
Brak zaliczenia z powodu nieobecności!
Gdybyśmy chcieli jedynie poinformować studenta, czy zaliczył ćwiczenia czy nie, moglibyśmy napisać taki oto kod:
missed_classes = 3
passed_tests = 3 # out of 4
class_participation = True
if (missed_classes <= 2) and (passed_tests >= 3) and class_participation:
print('Zaliczone!', 'Gratulacje!')
else:
print('Brak zaliczenia!')
Brak zaliczenia!
W kodzie dotyczącym zaliczenia ćwiczeń użyliśmy trzech funktorów logicznych: negacji (not
), koniunkcji (and
) i alternatywy (or
). Możemy je scharakteryzować za pomocą prawdy (True
) i fałszu (False
) jak poniżej.
warunek p |
warunek q |
and |
or |
---|---|---|---|
True |
True |
True |
True |
True |
False |
False |
True |
False |
True |
False |
True |
False |
False |
False |
False |
Pierwszy wiersz tabeli mówi, że jeśli warunki p
i q
są oba prawdziwe, to ich koniunkcja i alternatywa też jest prawdziwa. Drugi wiersz stwierdza, że jeśli warunek p
jest prawdziwy, a q
fałszywy, to ich koniunkcja jest fałszywa, zaś alternatywa jest prawdziwa. Itd.
warunek p | not p |
---|---|
True |
False |
False |
True |
Negacja warunku prawdziwego jest warunkiem fałszywym, zaś fałszywego prawdziwym.
Poniżej znajduje się program wystawiający ocenę w zależności od liczby zdobytych punktów.
print('Podaj liczbę zdobytych puntków: ')
points = int(input())
if points > 20:
print("Piątka! Brawo!")
elif points > 18:
print("Dobry +")
elif points > 16:
print("Dobry")
elif points > 14:
print("Dostateczny +")
elif points > 12:
print("Dostateczny")
else:
print("Dwója")
Podaj liczbę zdobytych puntków: 13 Dostateczny
Pętla jest instrukcją, która nakazuje komputerowi wykonać inne instrukcje kilkakrotnie. Instrukcje wykonują się w zapętleniu, czyli w kółko. Oczywiście komputer musi dokładnie wiedzieć
Pętla while
należy do tego drugiego typu. Oto przykład takiej pętli:
i = 1
while i <= 4:
print("Obrót pętli nr", i)
i = i + 1
Obrót pętli nr 1 Obrót pętli nr 2 Obrót pętli nr 3 Obrót pętli nr 4
Przyjrzyjmy się powyższemu przykładowi. Część while i <= 4:
zawiera słowo kluczowe while
po którym następuje warunek, który zamyka :
. Następnie mamy blok instrukcji do wykonania (oczywiście zaznaczony odpowiednim wcięciem). Schematycznie pętla while mogłaby wyglądać tak:
while WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
Czyż nie jest ona co do struktury podobna do instrukcji warunkowej:
if WARUNEK JEST PRAWDZIWY:
BLOK INSTRUKCJI DO WYKONANIA
Podobieństwo strukturalne pętli while
do instrukcji warunkowej niech nas nie zwiedzie. Działanie pętli while
jest bowiem zupełnie inne od działania instrukcji warunkowej. Jeżeli warunek pętli jest prawdziwy, następuje wykonanie bloku kodu wewnątrz pętli. Po wykonaniu bloku kodu, komputer powraca do ponownego sprawdzenia warunku. Jeżeli warunek pętli jest prawdziwy, następuje ponowne wykonanie bloku kodu, ale jeżeli warunek jest fałszywy, to blok kodu pętli while
zostaje pominięty, co tym samym kończy działanie pętli.
Jeśli teraz chwilę zastanowimy się nad działaniem pętli while
, to po chwili dojdziemy to ważnego wniosku: warunek pętli musi się jakoś zmieniać! W przeciwnym razie, jeśli tylko będzie prawdziwy, komputer po „wejściu do pętli” nigdy z niej nie wyjdzie.
Już wiemy co się może zmieniać… zmienna, a dokładnie rzecz ujmując, jej wartość. Popatrzymy jeszcze raz na nieco wcześniej zaprezentowaną pętlę while
. Warunek tej pętli zawiera zmienną i
- będziemy nazywać ją zmienną regulującą. Natomiast jeśli popatrzymy na blok instrukcji wewnątrz tej pętli, to na jego końcu znajdziemy instrukcję i = i + 1
. Zadaniem tej instrukcji jest zmiana wartości i
- w tym przypadku jest to inkrementacja o 1, przy każdym obrocie pętli. Aby pętla while
działała poprawnie i skutecznie, blok instrukcji wewnątrz pętli musi zawierać inkrementację (lub dekrementację) zmiennej regulujące.
Poniżej znajduje się kod prostej gry, który pomoże zrozumieć Ci sposób działania pętli while
(a przy okazji przypomni jak działają wcześniej omawiana przez nas elementy języka Python). Program losowo wybiera liczbę całkowita z przedziału od 1 do 100. Zadaniem użytkownika jest odgadnąć tę liczbę. Program podpowiada, czy poszukiwana liczba jest mniejsza od podanej, czy większa.
import random
number = random.randint(0,100)
print('Komputer właśnie wylosował liczbę z przedziału od 0 do 100.')
print('Zgadnij jaka to liczba!')
guess = int(input('Podaj liczbę od 0 do 100: '))
counter = 1
while guess != number:
if guess > 100:
print('Podana liczba jest większa niż 100!')
elif guess < 0:
print('Podana liczba jest mniejsza od 0!')
elif guess < number:
print(f'Podana liczba {guess} jest mniejsza od szukanej.')
else:
print(f'Podana liczna {guess} jest większa od szukanej.')
guess = int(input('Podaj liczbę od 0 do 100: '))
counter += 1 # counter = counter + 1
print(f'Gratulacje! Podana liczba to {number}!')
print(f'Potrzebowałaś/eś {counter} prób!')
Komputer właśnie wylosował liczbę z przedziału od 0 do 100. Zgadnij jaka to liczba! Podaj liczbę od 0 do 100: 34 Podana liczba 34 jest mniejsza od szukanej. Podaj liczbę od 0 do 100: 45 Podana liczba 45 jest mniejsza od szukanej. Podaj liczbę od 0 do 100: 56 Podana liczna 56 jest większa od szukanej. Podaj liczbę od 0 do 100: 55 Podana liczna 55 jest większa od szukanej. Podaj liczbę od 0 do 100: 54 Podana liczna 54 jest większa od szukanej. Podaj liczbę od 0 do 100: 53 Gratulacje! Podana liczba to 53! Potrzebowałaś/eś 6 prób!
Metoda random.randint(a, b)
zwraca losową liczbę całkowitą z przedziału od a
do b
, włączając skrajne liczby.
Poniższy kod rysuje uszy kota o dowolnym rozmiarze size
:
size = 10
i=1
while i <= size:
print((i*'@')+(((2*size)-(2*i))*' ')+(i*'@'))
i+=1
@ @ @@ @@ @@@ @@@ @@@@ @@@@ @@@@@ @@@@@ @@@@@@ @@@@@@ @@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@@@@@@@@
int()
- funkcja konwertuje określoną wartość, na przykład napis '7'
, na liczbę całkowitą.
import random
- komenda import pozwala na dostęp do dodatkowych instrukcji zapisanych w jakimś module; moduł random zawiera metody pozwalające na tworzenie liczb losowych.
random.randint()
- to przykład użycia metody randint()
z wcześniej zaimportowanego modułu random; random.randint()
zwraca losową liczbę całkowitą z zadanego przedziału włączając skrajne liczby.
Instrukcje break
i continue
wprowadzają zmianę w działaniu pętli.
Po napotkaniu instrukcji break
wewnątrz pętli program opuszcza pętlę. Przeanalizuj poniższy przykład.
i = 1
while i <= 10:
if i > 5:
break
print(i)
i += 1
1 2 3 4 5
Instrukcji continue
używamy w pętli do pominięcia wykonania części kodu wewnątrz pętli, który następuje po tej komendzie. Pętla nadal jest wykonywana zaczynając od początku. Przeanalizuj poniższy kod.
# continue
i = 1
while i <= 10:
if i >= 4 and i <= 7:
i += 1
continue
print(i)
i += 1
1 2 3 8 9 10
Instrukcja break zajmuje szczególne zastosowanie w pętli while
z warunkiem True
. Warunek True
jest zawsze spełniony, a zatem pętla while
z takim warunkiem będzie wykonywać się bez końca. Jedynym sposobem, aby z takiej pętli wyjść jest użycie instrukcji break
.
Poniższy kod „maszyny motywacyjnej” pozwoli zrozumieć ci rolę instrukcji break
w pętli while
z warunkiem True
. Zwróć uwagę, że pętla nie przestanie się wykonywać do momentu, gdy wprowadzisz 0
.
menu = '''
1 - motywacja
2 - więcej motywacji
3 - jeszcze więcej motywacji
0 - koniec
'''
while True:
print(menu)
letter = input('Wybierz: ')
if letter == '1':
print('\n\tJesteś zwycięzcą!')
elif letter == '2':
print('\n\tJesteś zwycięzcą! Jesteś zwycięzcą!')
elif letter == '3':
print('\n\tJesteś zwycięzcą! Jesteś zwycięzcą! Jesteś zwycięzcą!')
elif letter == '0':
print('\n\tDobiegłeś do mety i jesteś diamentem!')
break
else:
print('\n\tMusisz wybrać między 0, 1, 2 i 3!')
1 - motywacja 2 - więcej motywacji 3 - jeszcze więcej motywacji 0 - koniec Wybierz: 1 Jesteś zwycięzcą! 1 - motywacja 2 - więcej motywacji 3 - jeszcze więcej motywacji 0 - koniec Wybierz: 2 Jesteś zwycięzcą! Jesteś zwycięzcą! 1 - motywacja 2 - więcej motywacji 3 - jeszcze więcej motywacji 0 - koniec Wybierz: 3 Jesteś zwycięzcą! Jesteś zwycięzcą! Jesteś zwycięzcą! 1 - motywacja 2 - więcej motywacji 3 - jeszcze więcej motywacji 0 - koniec Wybierz: 0 Dobiegłeś do mety i jesteś diamentem!
Dopóki warunek jest prawdziwy, dopóty pętla while
jest wykonywana - tego dowiedzieliśmy się w poprzedniej sekcji. Pętla for
, którą teraz wprowadzimy, jest wykonywana określoną liczbę razy. Załóżmy, że chcemy wykonać jakiś blok kodu dokładnie 5
razy. Oto przykład jak możemy to zrobić:
for i in range(5):
print(f'{i}. Hello, world!')
0. Hello, world! 1. Hello, world! 2. Hello, world! 3. Hello, world! 4. Hello, world!
Funkcja range(5)
w powyższym kodzie odpowiada za wygenerowanie zakresu liczb od 0
do 4
. Zmienna i przyjmie więc kolejno wartości 0
, 1
, 2
, 3
i 4
. Czyli w przypadku podania tylko jednego argumentu: argument ten stanowi górne ograniczenie zakresu liczb (nie jest jednak wliczany do wygenerowanego zbioru liczb), a dolną granicę stanowi 0
. Zobacz to jeszcze raz:
for i in range(3):
print(i)
0 1 2
A oto sposób działania tej funkcji w przypadku podania dwóch liczb jako argumentów funkcji range:
for i in range(5, 10):
print(i)
5 6 7 8 9
Pierwsza z nich stanowi dolny zakres (włączając tę liczbę do zbioru), a drugi argument stanowi górną granicę (nie wliczając jej do tego zbioru).
Oto kolejny przykład użycia funkcji range()
; tym razem poza pętlą for
:
midlife_crisis = range(35,46)
current_year = 2022
my_age = current_year - 1979
if my_age in midlife_crisis:
print('Uwaga, może Cię dotknąć kryzys wieku średniego!')
Uwaga, może Cię dotknąć kryzys wieku średniego!
W przypadku podania trzech liczb: dwa pierwsze argumenty zachowują się tak, jak powyżej, natomiast trzeci jest wartością kroku, tzn. określa o ile zmienia się wartość zmiennej w pętli. Przeanalizuj przykłady dwóch pętli poniżej:
for i in range(0,60,10):
print(i)
0 10 20 30 40 50
for i in range(10,0,-2):
print(i)
10 8 6 4 2
Jak narysować górę dolarów?
number = 30
for i in range(number):
if i == (number // 2):
break
print((((number // 2) - (i + 1)) * ' ') + ((2 * i) + 1) * '$')
$ $$$ $$$$$ $$$$$$$ $$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$
range()
- funkcja zwraca sekwencję liczb (domyślnie zaczynając od 0) i zwiększa się (domyślnie) o 1 i zatrzymuje się przed określoną liczbą.
Programując będziemy bardzo często przetwarzać dane. Nie jest łatwo zdefiniować czym dane są i nie podejmiemy się tego zadania w tej książce. Wystarczy nam intuicja mówiąca, że danymi są w zasadzie wszelkie informacje zapisane w jakiś sposób. Danymi będą więc teksty prasowe, dane numeryczne w arkuszach kalkulacyjnych, dane osobowe zapisane bazach danych instytucji państwowych, raporty roczne spółek giełdowych, wyniki badań okresowych, nasze wpisy w serwisach społecznościowych, itd. W ostatnim czasie dużo mówi się o ochronie danych i ich wartości biznesowej (Data is money!). W pozostałych książkach z serii AI dla wszystkich pokazujemy jak sieci neuronowe i sieci semantyczne mogą pomóc nam w reprezentacji danych i wydobywaniu z nich niezwykle wartościowych informacji. Teraz jednak przejdźmy do podstawowych struktur danych jakie oferuje Python. Omówimy takie struktury danych jak: listy, słowniki, zbiory oraz krotki.
Do tej pory przechowywaliśmy pojedyncze wartości (czyli dane, takie jak napis lub liczba) w zmiennej. Struktury danych umożliwiają przechowywanie więcej niż jednej wartości w jednej zmiennej. Poza tym struktury danych pozwalają na grupowanie informacji oraz ich organizację.
Pierwszą strukturą danych jaką poznamy są listy. Doświadczenie pokazuje, że są one bardzo poręczne i bardzo często używane. Niekiedy nawet można odnieść wrażenie, że programowanie w języku Python, przynajmniej na początku, to w zasadzie ciągła praca z listami.
Sama nazwa mówi już wiele o tym, z czym mamy tu do czynienia. Lista to uporządkowany zbiór elementów (rzeczy, osób, zwierząt, etc.). Na przykład poniżej mamy listą z trzema elementami:
['element 1', 'element 2', 'element 3']
['element 1', 'element 2', 'element 3']
Listę definiujemy za pomocą nawiasów kwadratowych: [
oraz ]
. Elementy listy odzielamy od siebie przecinkami. Możemy tworzyć listy napisów, liczb lub obiektów różnych typów:
['to', 'jest', 'lista', 'napisów']
['to', 'jest', 'lista', 'napisów']
[1, 2, 3]
[1, 2, 3]
['napis', 1, True]
['napis', 1, True]
Listę możemy przypisać do zmiennej. Dzięki temu możemy odwołać się nie tylko do całej listy w sposób poręczny, ale również do poszczególnych jej elementów. Utwórzmy zmienną rainbow
(tęcza):
rainbow = []
W ten sposób przypisaliśmy do zmiennej rainbow
listę pustą, tzn. taką, która nie ma jeszcze żadnych elementów (metaforycznie można powiedzieć, że przygotowaliśmy czystą kartkę, na której dopiero będziemy tworzyć naszą listę).
Jak wiemy, tęcza składa się z 7
kolorów ułożonych w kolejności: czerwony, pomarańczowy, żółty, zielony, niebieski, indygo i fioletowy.
Do listy możemy dodawać elementy. Do tego służy metoda append()
. Przykład użycia:
rainbow.append('czerwony')
print(rainbow)
['czerwony']
Dodajmy następnie kolejny kolor tęczy:
rainbow.append('pomarańczowy')
print(rainbow)
['czerwony', 'pomarańczowy']
Zauważmy, że metoda append()
dodaje elementy zawsze na końcu listy.
Jeśli jeszcze raz wydarz Pythonowi komendę rainbow.append('pomarańczowy')
i wydrukujesz listę, to zobaczysz, że elementy w liście mogą się powtarzać.
rainbow.append('pomarańczowy')
print(rainbow)
['czerwony', 'pomarańczowy', 'pomarańczowy']
Gdybyśmy chcieli sprawdzić ile razy dany element pojawia się na naszej liście, możemy użyć metody count()
:
rainbow.count('pomarańczowy')
2
Oczywiście kolory w tęczy nie powtarzają się, więc musimy usunąć jedno wystąpienie koloru pomarańczowego. W tym przypadku nie ma znaczenia, który usuniemy. Do usuwania elementów z listy służy metoda remove()
. Musimy podać wartość, która ma zostać usunięta. Pamiętaj, że remove()
usuwa tylko pierwsze wystąpienie wartości na liście. Przykład użycia:
rainbow.remove('pomarańczowy')
print(rainbow)
['czerwony', 'pomarańczowy']
Teraz możemy dalej kontynuować dodawanie kolorów do tęczy.
rainbow.append('zielony')
print(rainbow)
['czerwony', 'pomarańczowy', 'zielony']
Pomyliliśmy się. Po pomarańczowym powinien być żółty. Musimy teraz albo 1) zamienić zielony na żółty i dodać jeszcze raz zielony po żółtym albo 2) wstawić kolor żółty pomiędzy pomarańczowy i zielony. Do obu z tych opcji potrzebujemy wiedzy o indeksach, o których jeszcze nie mówiliśmy w kontekście list, ale… już o nich wspominaliśmy przy okazji mówienia o napisach. Pamiętamy, że za pomocą komendy:
'Robert'[3]
'e'
mogliśmy wyodrębnić z napisu literę „e”. 3 to indeks tej litery w napisie 'Robert'
. Podobnie jest z listami. Każdy element listy ma swój indeks. Pamiętajmy, że wszelkie elementy w Pythonie indeksowane są zawsze od zera. Na przykład komenda
rainbow[1]
'pomarańczowy'
zwróci drugi element listy (pomarańczowy). Indeksem koloru zielonego jest aktualnie 2, co można sprawdzić za pomocą komendy:
rainbow[2]
'zielony'
lub
rainbow.index('zielony')
2
Tak na marginesie dodajmy, że możemy również użyć indeksów ujemnych, by uzyskiwać dostęp do elementów listy od końca:
rainbow[-1]
'zielony'
Powyższa komenda zwróci ostatni element listy, czyli aktualnie kolor zielony.
Wróćmy jednak do naszego problemu. Mamy aktualnie listę
rainbow
['czerwony', 'pomarańczowy', 'zielony']
i dwie strategie działania:
Zastosowanie pierwszej z nich, czyli zamiana zielonego na żółty, polega na zastąpieniu danego element listy pod danym indeksem (w naszym przypadku koloru zielonego) przez element nowy (w naszym przypadku, kolor żółty). Wyżej wspomnieliśmy, że aby sprawdzić indeks elementu, używamy metody index()
.
rainbow.index('zielony')
2
Wynikiem będzie 2. Znając indeks możemy wykonać następującą instrukcję:
rainbow[2] = 'żółty'
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty']
lub tak, wiedząc, że chcemy zastąpić ostatni element:
rainbow[-1] = 'żółty'
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty']
Nasza lista wygląda teraz tak:
['czerwony', 'pomarańczowy', 'żółty']
Wprawdzie kolejność jest prawidłowa, ale straciliśmy kolor zielony
. Musimy go dodać jeszcze raz:
rainbow.append('zielony')
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty', 'zielony']
Kontynuujmy dodawanie kolorów:
rainbow.append('indygo')
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'indygo']
Znów pomyliliśmy kolejność. Po zielonym powinien być niebieski. Wiemy już jak podmieniać elementy, ale użyta przez nas wcześniej strategia nie wydaje się najlepsza, ponieważ zastępując element jednocześnie go usuwa. Spróbujmy teraz drugiej strategii, czyli wstawienia nowego elementu pomiędzy dwa elementy listy. Użyjemy do tego metody insert()
. Metoda ta wymaga podania indeksu, w miejsce którego chcemy wstawić element oraz nazwę elementu, który chcemy do listy dodać. Chcemy wstawić „niebieski” w miejsce, w którym teraz jest kolor indygo. To pozycja o indeksie 4:
rainbow.insert(4, 'niebieski')
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'niebieski', 'indygo']
Jak widać, metoda insert()
nie usunęła indygo, ale przesunęła go o jedno miejsce. Kolor indygo
ma teraz indeks 5. Sprawdź to używając metody index()
.
Pozostało nam dodanie ostatniego koloru do naszej tęczy:
rainbow.append('fioletowy')
Wyświetlmy zatem elementy naszej listy. Możemy użyć do tego pętli for z listą:
for color in rainbow:
print(color)
czerwony pomarańczowy żółty zielony niebieski indygo fioletowy
Dokładnie przeanalizuj tę pętlę. Zauważ, że pętla for
korzysta bezpośrednio z listy przechodząc kolejno po wszystkich jej elementach.
Gdybyśmy chcieli zwrócić kolejno wszystkie elementy tęczy wraz z ich pozycją możemy skorzystać z funkcji enumerate()
. Funkcja zwraca elementy listy wraz z ich indeksami. Poniższy kod wyświetli elementy naszej tęczy wraz z numerem wskazującym na jego kolejność:
for index, color in enumerate(rainbow):
print(index+1,'-',color)
1 - czerwony 2 - pomarańczowy 3 - żółty 4 - zielony 5 - niebieski 6 - indygo 7 - fioletowy
Oto jeszcze kilka metod, o których warto wspomnieć w kontekście list.
Do usuwania elementów z listy służy również del
. Należy podać indeks elementu, który ma zostać usunięty. W przypadku podania indeksu elementu, który nie znajduje się na liście, zostaje zgłoszony błąd.
del rainbow[-1]
print(rainbow)
['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'niebieski', 'indygo']
Różnica między użyciem del
i wcześniej omówioną metodą remove()
, nie licząc innego sposobu zapisywania tych instrukcji, polega na tym, że w del
podajemy indeks elementu, który ma zostać usunięty (pozycję na liście), a w przypadku remove()
podajemy samą wartość, która ma zostać usunięta.
Za pomocą funkcji len()
można sprawdzić wielkość listy, czyli określić, ile elementów znajduje się w liście (pamiętamy, że tę samą funkcję używaliśmy wcześniej do sprawdzenia liczby znaków w napisie). Przykład użycia tej funkcji:
len(rainbow)
6
Jeżeli chcemy sprawdzić, czy jakiś element znajduje się na liście, możemy użyć instrukcji if
z operatorem in
:
if 'żółty' in rainbow:
print("Mamy kolor żółty w tęczy.")
else:
print("Nie ma koloru żółtego w tęczy.")
if 'biały' in rainbow:
print("Mamy kolor biały w tęczy.")
else:
print("Nie ma koloru białego w tęczy.")
Mamy kolor żółty w tęczy. Nie ma koloru białego w tęczy.
Powyższe instrukcje są wyrażeniami warunkowymi if
i obowiązują je przedstawione wcześniej zasady. Operator in sprawdza, czy dany element znajduje się na liście.
Kolejność elementów listy jest zgodna z kolejnością ich dodawania do listy. Gdybyśmy chcieli tę kolejność zmienić, na przykład ustawić elementy listy alfabetycznie, możemy listę posortować. Do sortowania elementów na liście służy metoda sort()
. Domyślnie elementy sortowane są od najmniejszego do największego.
Ustawmy kolory naszej tęczy w porządku alfabetycznym. Zacznijmy od stworzenia nowej tęczy o nazwie sorted_raibow
. Skopiujmy do niej kolory z tęczy rainbow
. Poniższy kod uporządkuje kolory w porządku alfabetycznym:
sorted_rainbow = rainbow.copy() # sorted_rainbow = rainbow[:]
sorted_rainbow.sort()
print(sorted_rainbow)
['czerwony', 'indygo', 'niebieski', 'pomarańczowy', 'zielony', 'żółty']
Aby uporządkować listę w porządku odwrotnym do alfabetycznego użyjemy reverse=True
jak pokazano poniżej:
sorted_rainbow = rainbow.copy()
sorted_rainbow.sort(reverse=True)
print(sorted_rainbow)
['żółty', 'zielony', 'pomarańczowy', 'niebieski', 'indygo', 'czerwony']
Gdybyśmy chcieli odwrocić kolejność kolorów tęczy skorzystam z metody reverse()
:
reversed_rainbow = rainbow.copy()
reversed_rainbow.reverse()
print(reversed_rainbow)
['indygo', 'niebieski', 'zielony', 'żółty', 'pomarańczowy', 'czerwony']
A gdybyśmy życzyli sobie wymieszać kolory tęczy tak, aby ich kolejność była losowa, użyjemy metody random.shuffle()
:
import random
random.shuffle(rainbow)
print(rainbow)
['niebieski', 'zielony', 'żółty', 'indygo', 'pomarańczowy', 'czerwony', 'fioletowy']
Metoda pop()
usuwa element o podanym indeksie jednocześnie zwracając usuwany element. Na przykład poniższa instrukcja usunie z listy rainbow
ostatni element i go wyświetli:
print(rainbow.pop(-1))
indygo
Jeśli chcemy usunąć wszystkie elementy listy używamy metody clear()
:
rainbow.clear()
print(rainbow)
[]
Powyższy kod zwróci listę pustą, czyli []
.
Mając dwie listy możemy je połączyć lub innymi słowy, rozszerzyć jedną listę o elementy drugiej listy. Służy do tego metoda extend()
:
languages = ['Polish', 'English']
languages_to_add = ['Spanish', 'Italian']
languages.extend(languages_to_add)
print(languages)
['Polish', 'English', 'Spanish', 'Italian']
Podobnie jak w przypadku napisów również dla list możemy używać „krajania” (ang. slicing). Na przykład możemy „wykroić” z listy jakąś jej część, jak to pokazano niżej
rainbow = ['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'niebieski', 'indygo', 'fioletowy']
rainbow[:3]
['czerwony', 'pomarańczowy', 'żółty']
Zbliżając się do końca tego rozdziału omówmy jeszcze dwie ważne metody split()
i join()
.
Metoda split()
zamienia napis w listę, której elementami są fragmenty tego napisu. Sposób dzielenia napisu określa separator, którym domyślnie jest spacja. Utwórzmy zmienną paragraph
i przypiszmy jej fragment tekstu zaczerpniętego z Wikipedii (https://pl.wikipedia.org/wiki/Tęcza):
paragraph = '''Pomimo faktu, że w tęczy występuje niemal ciągłe widmo kolorów,
wyszczególnia się z reguły następujące barwy:
czerwony (na zewnątrz łuku), pomarańczowy, żółty, zielony,
niebieski, indygo i fioletowy (wewnątrz łuku).'''
paragraph_split = paragraph.split()
print(paragraph_split)
['Pomimo', 'faktu,', 'że', 'w', 'tęczy', 'występuje', 'niemal', 'ciągłe', 'widmo', 'kolorów,', 'wyszczególnia', 'się', 'z', 'reguły', 'następujące', 'barwy:', 'czerwony', '(na', 'zewnątrz', 'łuku),', 'pomarańczowy,', 'żółty,', 'zielony,', 'niebieski,', 'indygo', 'i', 'fioletowy', '(wewnątrz', 'łuku).']
paragraph_split = paragraph.split('\n')
print(paragraph_split)
['Pomimo faktu, że w tęczy występuje niemal ciągłe widmo kolorów, ', 'wyszczególnia się z reguły następujące barwy: ', 'czerwony (na zewnątrz łuku), pomarańczowy, żółty, zielony, ', 'niebieski, indygo i fioletowy (wewnątrz łuku).']
Metoda join()
łączy elementy listy w napis. Łączenie wykonujemy za pomocą zadanego przez nas separatora.
' - '.join(rainbow)
'czerwony - pomarańczowy - żółty - zielony - niebieski - indygo - fioletowy'
Na koniec poznajmy jeszcze funkcję zip()
. Pozwala ona na iterowanie po dwóch lub więcej listach jak to pokazano poniżej:
rainbow = ['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'niebieski', 'indygo', 'fioletowy']
friends = ['Jan', 'Kasia', 'Zosia', 'Michał', 'Zuzia', 'Marysia', 'Wojtek']
for color, friend in zip(rainbow, friends):
print(friend, '-', color)
Jan - czerwony Kasia - pomarańczowy Zosia - żółty Michał - zielony Zuzia - niebieski Marysia - indygo Wojtek - fioletowy
Poniższy kod dzieli tekst na słowa i prezentuje dwie listy słów: przed i po oczyszczeniu:
sentence = 'Ala ma kota, a kot ma mysz; i co z tego? A to z tego, że nic.'
words = sentence.split()
signs_to_remove = '.,;!?()"\'*%$#@'
clean_words = []
for word in words:
clean_word =''
for sign in word:
if sign not in signs_to_remove:
clean_word = clean_word + sign
clean_words.append(clean_word)
for w1, w2 in zip(words, clean_words):
print(w1,'\t', w2)
Ala Ala ma ma kota, kota a a kot kot ma ma mysz; mysz i i co co z z tego? tego A A to to z z tego, tego że że nic. nic
append()
- metoda dodaje element na koniec listy.
clear()
- metoda usuwa wszystkie elementy z listy.
copy()
- metoda zwraca kopię listy.
count()
- metoda zwraca, ile razy określony element pojawił się na liście.
del - służy m.in. do usuwania elementów listy; należy podać indeks elementu, który ma zostać usunięty. W przypadku podania indeksu elementu, który nie znajduje się na liście, zostaje zgłoszony błąd.
enumerate()
- funkcja zwraca elementy listy wraz z ich indeksami.
extend()
- metoda dodaje do końca listy wszystkie elementy innej listy.
index()
- metoda zwraca indeks określonego elementu na liście.
join()
- metoda pobiera wszystkie elementy listy i łączy je w jeden ciąg elementów oddzielonych zadanym separatorem (na przykład spacją).
len() - funkcja zwraca liczbę elementów listy.
pop()
- metoda usuwa element o podanym indeksie z listy i zwraca usunięty element.
random.shuffle()
- metoda ustawia kolejność elementów listy w sposób losowy.
remove()
- metoda usuwa pierwszy pasujący element z listy i zwraca błąd jeśli ustanego elementu nie ma na liście.
reverse()
- metoda odwraca kolejność elementów listy.
sort()
- metoda sortuje elementy danej listy w kolejności rosnącej lub malejącej.
split()
- metoda dzieli napis za pomocą określonego separatora (domyślnie jest to spacja) i zwraca listę napisów.
zip()
- funkcja pobiera elementy z kilku list, agreguje je i zwraca.
Słowniki to struktury danych, w których informacje przechowywane są w postaci par klucz:wartość
. Słowniki w Pythonie, podobnie zresztą jak analogiczne struktury danych w innych językach programowania, działają na tej zasadzie, że na podstawie klucza wyszukiwana jest wartość.
Do tworzenia słowników służą nawiasy klamrowe: {
oraz }
. Klucz od wartości oddziela się znakiem :
, a poszczególny wpisy w słowniku oddzielane są przecinkami. Najlepiej widać to na przykładzie:
rainbow_dict = {1:'czerwony',
2:'pomarańczowy',
3:'żółty',
4:'zielony',
5:'niebieski',
6:'indygo',
7:'fioletowy'}
Do elementów słownika odwołujemy się za pomocą kluczy. W powyższym przykładzie kluczami są liczby od 1 do 7 (choć kluczami mogą być również napisy). Poniższy kod zwraca napis czerwony
.
rainbow_dict[1]
'czerwony'
Warto nadmienić, że klucze nie są indeksami, a porządek par (klucz, wartość), nie ma znaczenia.
Metoda items()
zwróci wszystkie elementy słownika w postaci par (klucz, wartość) w postaci specjalnego widoku:
rainbow_dict.items()
dict_items([(1, 'czerwony'), (2, 'pomarańczowy'), (3, 'żółty'), (4, 'zielony'), (5, 'niebieski'), (6, 'indygo'), (7, 'fioletowy')])
Widot ten jest iterowalny, tzn. możemy po nim iterować:
for item in rainbow_dict.items():
print(item)
(1, 'czerwony') (2, 'pomarańczowy') (3, 'żółty') (4, 'zielony') (5, 'niebieski') (6, 'indygo') (7, 'fioletowy')
Metoda keys()
zwróci wszystkie klucze danego słownika w postaci specjalnego widoku:
rainbow_dict.keys()
dict_keys([1, 2, 3, 4, 5, 6, 7])
Metoda values()
zwróci wszystkie wartości danego słownika w postaci specjalnego widoku:
rainbow_dict.values()
dict_values(['czerwony', 'pomarańczowy', 'żółty', 'zielony', 'niebieski', 'indygo', 'fioletowy'])
Podobnie jak w przypadku list, metoda clear()
usuwa wszystkie elementy słownika, copy()
tworzy jego kopię, pop() usuwa element słownika o zadanym kluczu i go zwraca. Metoda update()
— podobnie jak extend()
w listach - dodaje do słownika elementy innego słownika.
Samodzielnie napisz kod wykorzystujący te metody.
Metoda get()
zwraca wartość dla zadanego klucza. Oprócz klucza można podać jako argument komunikat, który ma zwrócić Python w przypadku braku elementu o zadanym kluczu.
rainbow_dict.get(7)
'fioletowy'
Poniższy kod zwróci napis o treści „Nie ma tylu kolorów w tęczy!”.
rainbow_dict.get(8, 'Nie ma tylu kolorów w tęczy!')
'Nie ma tylu kolorów w tęczy!'
Poniżej znajduje się przykład słownika, którego kluczami są napisy:
pl_en_dict = {'mama':'mother',
'tata':'dad',
'syn':'son',
'córka':'daughter',
'ciocia':'aunt'}
pl_en_dict.get('mama')
'mother'
clear()
- metoda usuwa wszystkie elementy z listy.
copy()
- metoda zwraca kopię listy.
dict()
- funkcja tworzy słownik.
items()
- metoda zwraca obiekt widoku, który wyświetla listę par/krotek (klucz, wartość).
keys()
- metoda zwraca obiekt widoku, który wyświetla listę wszystkich kluczy słownika.
values()
- metoda zwraca obiekt widoku, który wyświetla listę wszystkich wartości słownika.
get()
- metoda zwraca wartość określonego klucza, jeśli klucz znajduje się w słowniku.
pop()
- metoda usuwa element o podanym indeksie z listy i zwraca usunięty element.
update()
- metoda dodaje do końca listy wszystkie elementy innej listy.
popitem()
- metoda usuwa ostatni element dodany do słownika i go zwraca.
fromkeys()
- metoda tworzy nowy słownik z podanej sekwencji elementów z wartością podaną przez użytkownika.
Zbiory są podobne do list, ale w ich przypadku kolejność elementów nie ma znaczenia. Podobnie jak to miało miejsce z listami, zbiór można utworzyć wymieniając jego elementy. Utwórzmy zbiór muzyków występujących w pierwszym składzie zespołu Hey w roku 1992:
hey_1992 = {'Katarzyna Nosowska', 'Marcin Żabiłowicz', 'Robert Ligiewicz', 'Marcin Macuk', 'Piotr Banach'}
Skład zespołu ulegał kilku zmianom (zob. https://pl.wikipedia.org/wiki/Hey#Muzycy). Utwórzmy więc zmienną hey_band
, dzięki której zilustrujemy te zmiany:
hey_band = set()
Metoda update()
dodaje do zbioru elementy innego zbioru (podobnie jak to maiło miejsce ze słownikami). Dodajmy więc do aktualnie pustego zbioru hey_band skład wyjściowy z roku 1992:
hey_band.update(hey_1992)
print(hey_band)
{'Marcin Żabiłowicz', 'Piotr Banach', 'Robert Ligiewicz', 'Marcin Macuk', 'Katarzyna Nosowska'}
W roku 1993 z zespołu odszedł basista Marcin Macuk, a dołączył w jego miejsce Jacek Chrzanowski. Użyjemy metody remove()
, aby usunąć element ze zbioru i metody add()
, aby do zbioru dodać nowy element:
hey_band.remove('Marcin Macuk')
hey_band.add('Jacek Chrzanowski')
Następnie utworzymy kopię tego składu za pomocą metody copy()
i przypiszemy ją do zmiennej hey_1993
:
hey_1993 = hey_band.copy()
print(hey_1993)
{'Marcin Żabiłowicz', 'Jacek Chrzanowski', 'Piotr Banach', 'Robert Ligiewicz', 'Katarzyna Nosowska'}
Zapamiętaj, że dodawanie elementu, który już jest w zbiorze, nie zmienia tego zbioru (inaczej niż miało to miejsce w przypadku list, gdy elementy mogły się powtarzać - w zbiorze elementy nigdy się nie powtarzają). I jeszcze jedna uwaga. Możesz usuwać elementy ze zbioru również za pomocą metody discard()
. Ta metoda nie zwróci błędu jeśli zechcesz usunąć element zbioru, którego tam niema. Metoda remove()
w takiej sytuacji zwróci błąd.
Następnie nastąpiły kolejne dwie ważne zmiany w zespole. Najpoważniejszą było odejście Piotra Banacha w roku 1999 roku:
hey_band.remove('Piotr Banach')
hey_1999 = hey_band.copy()
print(hey_1999)
{'Marcin Żabiłowicz', 'Robert Ligiewicz', 'Jacek Chrzanowski', 'Katarzyna Nosowska'}
W roku 2001 do Hey dołączył Paweł Krawczyk:
hey_band.add('Paweł Krawczyk')
hey_2001 = hey_band.copy()
print(hey_2001)
{'Marcin Żabiłowicz', 'Jacek Chrzanowski', 'Robert Ligiewicz', 'Paweł Krawczyk', 'Katarzyna Nosowska'}
A w 2012 do Hey na chwilę również dołączył Marcin Zabrocki:
hey_band.add('Marcin Zabrocki')
hey_2012 = hey_band.copy()
print(hey_2012)
{'Marcin Żabiłowicz', 'Jacek Chrzanowski', 'Robert Ligiewicz', 'Paweł Krawczyk', 'Marcin Zabrocki', 'Katarzyna Nosowska'}
Zespół Hey zawiesił swoją działalność w październiku 2017. Nie było już nim Marcina Zabrockiego, który był członkiem zespołu jedynie przez rok. Rok przed zawieszeniem, do zespołu wrócił Marcin Macuk:
hey_band.remove('Marcin Zabrocki')
hey_band.add('Marcin Macuk')
hey_2017 = hey_band.copy()
print(hey_2017)
{'Marcin Żabiłowicz', 'Jacek Chrzanowski', 'Robert Ligiewicz', 'Paweł Krawczyk', 'Marcin Macuk', 'Katarzyna Nosowska'}
Udało nam się utworzyć kilka zbiorów reprezentujących składy zespołu Hey w ciagu jego trwania. Teraz pokażemy, że na zbiorach możemy wykonywać ciekawe operacje.
Na przykład możemy sumować zbiory za pomocą metody union()
. Poniższy kod utworzy zbiór wszystkich muzyków, którzy kiedykolwiek byli członkami zespołu Hey:
set.union(hey_1992, hey_1993, hey_1999, hey_2001, hey_2012, hey_2017)
{'Jacek Chrzanowski', 'Katarzyna Nosowska', 'Marcin Macuk', 'Marcin Zabrocki', 'Marcin Żabiłowicz', 'Paweł Krawczyk', 'Piotr Banach', 'Robert Ligiewicz'}
Możemy też stworzyć zbiór złożony z członków pierwszego i ostatniego składu:
hey_1992.union(hey_2017)
{'Jacek Chrzanowski', 'Katarzyna Nosowska', 'Marcin Macuk', 'Marcin Żabiłowicz', 'Paweł Krawczyk', 'Piotr Banach', 'Robert Ligiewicz'}
Gdybyśmy chcieli sprawdzić, którzy muzycy byli we wszystkich składach zespołu, to powinniśmy poszukać części wspólnej wszystkich składów. Do tego służy metoda intersection()
:
set.intersection(hey_1992, hey_1993, hey_1999, hey_2001, hey_2012, hey_2017)
{'Katarzyna Nosowska', 'Marcin Żabiłowicz', 'Robert Ligiewicz'}
Może nas również interesować, jacy muzycy byli w pierwszym składzie zespołu Hey, a których nie było w składzie finalnym z 2017 roku. Do tego służy metoda difference()
:
hey_1992.difference(hey_2017)
{'Piotr Banach'}
Muzycy, którzy zmienili się między pierwszym i ostatnim składem:
hey_1992.symmetric_difference(hey_2017) # (X u Y) - (X n Y)
{'Jacek Chrzanowski', 'Paweł Krawczyk', 'Piotr Banach'}
Gdbyśmy uznali, że ponieważ Hey zawiesił swoją działalność, to w zasadzie nie ma w nim już żądnych muzyków, wówczas możemy wyczyścić zbiór ze wszystkich elementów za pomocą metody clear()
:
hey_band.clear()
isdisjoint()
zwraca wartość True
, jeśli zbiory są rozłączne, tj. nie mają elementów wspólnych. Czy pierwszy skład był całkowicie różny od ostatniego? Sprawdźmy:
hey_1992.isdisjoint(hey_2017)
False
issubset()
zwraca wartość True
, jeśli zbiór zawiera się w drugim, tj. wszystkie elementy jednego zbioru są również elementami drugiego (choć nie musi być odwrotnie):
hey_2001.issubset(hey_2017)
True
hey_2017.issubset(hey_2001)
False
clear()
- metoda usuwa wszystkie elementy ze zbioru.
copy()
- metoda zwraca kopię zbioru.
difference() - metoda zwraca zbiór będący różnicą dwóch zbiorów.
discard()
- metoda usuwa określony element ze zbioru (jeśli jest obecny); metoda nie zwróci błędu jeśli zechcesz usunąć element zbioru, którego niema.
intersection() - metoda zwraca nowy zbiór zawierający elementy wspólne dla dwóch lub więcej zbiorów.
isdisjoint()
- metoda zwraca True
, jeśli dwa zbiory są rozłączne. Jeśli nie, zwraca False
.
issubset()
- metoda zwraca True
, jeśli wszystkie elementy zbioru są obecne w innym zbiorze (przekazywanym jako argument). Jeśli nie, zwraca False
.
remove()
- metoda usuwa określony element ze zbioru; metoda zwróci błąd jeśli zechcesz usunąć element zbioru, którego tam niema.
union()
- metoda zwraca nowy zbiór z elementami ze wszystkimi zbiorów będących jej argumentami.
update()
- metoda dodaje do końca zbioru wszystkie elementy innego zbioru.
issuperset()
- metoda zwraca wartość True
, jeśli zbiór jest nadzbiorem drugiego
pop()
- metoda usuwa dowolny element ze zbioru i zwraca ten element.
frozenset()
- funkcja tworzy zbiór niezmienny, tzn. taki, którego elementów nie można zmienić (dodać lub usunąć).
Krotki (ang. tuple) podobnie jak listy służą do przechowywania informacji o kilku elementach jednocześnie. W krotkach podobnie jak w listach kolejność elementów ma znaczenie. To co różni krótki od list to nawiasy, w których zapisujemy elementy: (
i )
. Kolejną różnicą jest fakt, że nie można zmieniać rozmiaru i zawartości raz utworzonej krotki (co wyrażamy angielskim terminem immutable).
Za pomocą krotek możemy na przykład przechowywać informacje of kulturystach:
jan_kaszanka = ('Jan', 'Kaszanka', 180, 113, 54, 150)
jan_kaszanka
('Jan', 'Kaszanka', 180, 113, 54, 150)
W powyższej krotce na kolejnych miejscach mamy informację dotyczące odpowiednio: imienia, nazwiska, wzrostu, wagi, obwodu bicepsa oraz obwodu klatki piersiowej. Gdyby rozmiary bicepsa lub klatki piersiowej Jana Kaszanki zmieniły się musielibyśmy zmiennej jan_kaszanka
przypisać nową krotkę (update nie jest możliwy!):
jan_kaszanka = ('Jan', 'Kaszanka', 180, 113, 55, 150)
Dla krotek Pyton oferuje dwie metody: count()
oraz index()
. count()
która zwraca ile razy dany element występuje w danej krotce:
jan_kaszanka.count(180)
1
Metodę index()
zwraca indeks zadanego elementu. Używając metody index()
możemy podać trzy argumenty: element, którego szukamy, indeks od którego zaczniemy wyszukiwanie oraz indeks na którym zakończymy wyszukiwanie:
jan_kaszanka.index(180, 2, 3)
2
W powyższym przykładzie szukamy w krotce wartości 180
zaczynając od indeksu 2
i kończąc na indeksie 3
.
Krotki są iterowalne:
for i in jan_kaszanka:
print(i)
Jan Kaszanka 180 113 55 150
Listy, słowniki, zbiory oraz krotki są kolekcjami (ang. collections). Cześć metod, o których pisaliśmy w tym rozdziale ma szerszy zakres, tj. nie stosuje się tylko do list lub tylko do słowników, ale do wszystkich kolekcji. Poniżej pokażemy przykładowo, że argumentem metody extend()
dla list może być dowolna kolekcja.
l = [1,2] # lista
s = {3,4} # zbiór
t = (5,6) # krotka
d = {7:"buh", 8:"buha"} # słownik
l.extend(s)
print(l)
l.extend(t)
print(l)
l.extend(d)
print(l)
[1, 2, 3, 4] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6, 7, 8]
Podobnie argumentem metody sorted()
dla list może być dowolna kolekcja:
sorted({2, 1, 4, 3})
[1, 2, 3, 4]
Trzy poniższe linie kodu mają ten sam wynik, zbiór {1, 2, 3, 4}
:
{1,2}.union([3,4])
{1,2}.union((3,4))
{1,2}.union({3:"buh", 4:"buha"})
{1, 2, 3, 4}
Podobnie możemy użyć kolekcji z metodami stosowanymi do zbiorów: intersection()
, difference()
, symmetric_difference()
, issubset()
oraz issuperset()
.
Możemy też utworzyć niezmienny zbiór za pomocą funkcji frozenset()
, z dowolnej kolekcji. Podobnie funkcja enumerate()
, którą używaliśmy w pętli for może przyjąć jako argument dowolną kolekcję, np. zbiór.
Mechanizm składania (ang. comprehension) ułatwia tworzenie list, słowników oraz zbiorów i należy do tych aspektów języka Python, które łatwiej jest pokazać niż wytłumaczyć.
Listę składaną (ang. list comprehension) możemy utworzyć z dowolnej kolekcji. Załóżmy, że mamy listę cen trzech produktów:
prices = [100, 150, 200]
Gdbyśmy chcieli utworzyć nową listę z cenami zwiększonymi o 20%
, o nazwie prices_increased
, to korzystając z dotychczas poznanych technik moglibyśmy zrobić tak:
prices_increased = []
for price in prices:
prices_increased.append(price * 1.20)
prices_increased
[120.0, 180.0, 240.0]
Używając list składanych moglibyśmy to samo zrobić za pomocą jednej linii kodu w następujący sposób:
[price * 1.20 for price in prices]
[120.0, 180.0, 240.0]
Słowniki składane (ang. dictionary comprehension) tworzymy podobnie jak listy składane. Załóżmy, że mamy dwie listy, listę owoców i ich cen:
fruits = ['mandarynki', 'jabłka', 'kiwi']
prices = [15, 10, 20]
Słownik, którego kluczami są nazwy owoców, a wartościami ich ceny tworzymy następująco:
fruits_prices_dict = {fruit:price for fruit, price in zip(fruits, prices)}
fruits_prices_dict
{'mandarynki': 15, 'jabłka': 10, 'kiwi': 20}
Słowniki składane są poręczne, gdy chcemy zamienić w już istniejącym słowniku klucze na wartości. Wówczas robimy tak:
{value:key for key, value in fruits_prices_dict.items()}
{15: 'mandarynki', 10: 'jabłka', 20: 'kiwi'}
Zbiory składane (ang. set comprehension) tworzymy w dokładnie taki sam sposób. Zadaniem poniższego kodu jest stworzenie zbioru słów znajdujących się we fragmecie wiersza ,,Szybko'' Danuty Wawiłow.
strophe = '''
Szybko, zbudź się, szybko, wstawaj!
Szybko, szybko, stygnie kawa!
Szybko, zęby myj i ręce!
Szybko, światło gaś w łazience!
Szybko, tata na nas czeka!
Szybko, tramwaj nam ucieka!
Szybko, szybko, bez hałasu!
Szybko, szybko, nie ma czasu!
Na nic nigdy nie ma czasu?
'''
bag_of_words = {word for word in strophe.lower().replace('.', '').replace(',', '').replace('!', '').split()}
bag_of_words
{'bez', 'czasu', 'czasu?', 'czeka', 'gaś', 'hałasu', 'i', 'kawa', 'ma', 'myj', 'na', 'nam', 'nas', 'nic', 'nie', 'nigdy', 'ręce', 'się', 'stygnie', 'szybko', 'tata', 'tramwaj', 'ucieka', 'w', 'wstawaj', 'zbudź', 'zęby', 'łazience', 'światło'}
W poprzednim rozdziale mówiliśmy danych i o strukturach (takich jak listy, słowniki, zbiory oraz krotki) służących do organizowania danych. Powiedzieliśmy, że danymi są wszelkie informacje zapisane w jakiś sposób. Teraz dodamy, że dane są najczęściej zapisywane w plikach. Dokumenty, które tworzymy w takich programach jak Word, czy arkusze kalkulacyjne, to tylko niektóre przykłady typów plików, z którymi spotykamy się niemalże na co dzień.
Pliki możemy podzielić roboczo na dwie grupy, te które narzucają danym pewną strukturę, dzięki której nota bene możemy na tych danych dokonywać pewnych operacji (tu mamy na przykład arkusze kalkulacyjne) oraz te, które nie posiadają struktury dla danych które przechowują (tu mamy zwykłe pliki tekstowe).
Z punktu widzenia Pythona, na pliku możemy wykonać jedną z trzech operacji:
r
.a
. Operacja ta tworzy plik, jeśli plik do którego chcemy coś dodać nie istnieje.w
. Operacja ta tworzy plik, jeśli plik w którym chcemy coś zapisać nie istnieje oraz nadpisuje istniejącą treść o ile plik istnieje.Tworzymy listę zakupów i dodajemy do niej produkty:
with open('lista_zakupów.txt', mode='w') as file:
file.write('Lista zakupów:\n')
while True:
product = input('Dodaj produkt: ')
if product:
file.write(f'- {product}\n')
else:
break
Dodaj produkt: masło Dodaj produkt: chleb Dodaj produkt:
Odczytujemy listę zakupów:
with open('lista_zakupów.txt', 'r') as file:
print(file.read())
Lista zakupów: - masło - chleb
with open('lista_zakupów.txt', 'r') as file:
for line in file:
print(line)
Lista zakupów: - masło - chleb
Praca na liniach pliku poza blokiem with
:
with open('lista_zakupów.txt', 'r') as file:
lines = file.readlines()
for line in lines:
print(line)
Lista zakupów: - masło - chleb
Plik csv (ang. comma separated values) jest plikiem tekstowym, w którym informacje są uporządkowane w formie tabularycznej. Możemy więc traktować plik csv jako tabelę, w której mamy nazwy kolumn w pierwszym wierszu i to one nadają sens danym. Dane są zwykle oddzielone przecinkami, choć możliwe jest ustalenie innego separatora (ang. delimiter).
Pliki csv można czytać jak normalne pliki tekstowe. Są również biblioteki nieco ułatwiające czytanie takich plików i zapisywanie w nich informacji.
import csv
with open('students.csv', 'w') as file:
csv_writer = csv.writer(file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
csv_writer.writerow(['Jan', 'Kiełbasa', '12.12.2000', 'Białystok'])
csv_writer.writerow(['Monika', 'Szacka', '16.04.2000', 'Lublin'])
Największy problem z plikami csv dotyczy danych (np. napisów lub liczb), które w swojej strukturze zawierają separator. Na przykład liczba 11,235
czy napis Cześć, co u ciebie?
zapisane do pliku csv bez cudzysłowu będą stanowiły realny problem.
Stąd używając writer
a określamy, czy i jak chcemy używać cudzysłowu. Definicja naszego writer
a wygląda tak, że quoting=csv.QUOTE_MINIMAL
, co oznacza, że cudzysłów zostanie użyty w sytuacji, gdy w danych będziemy mieli separator (czyli przecinek w naszym przypadku) lub znak przypisany do parametru quotechar
. csv.QUOTE_ALL
zamieści wszystkie dane w cudzysłowie.
Odczytując dane musimy również ustalić atrybuty parametrów delimiter
i quotechar
.
with open('students.csv', newline='') as file:
csv_reader = csv.reader(file, delimiter=',', quotechar='"')
for row in csv_reader:
print(f'Imię: {row[0]}\nNazwisko: {row[1]}\nData urodzenia: {row[2]}\nMiejsce urodzenia: {row[3]}\n')
Imię: Jan Nazwisko: Kiełbasa Data urodzenia: 12.12.2000 Miejsce urodzenia: Białystok Imię: Monika Nazwisko: Szacka Data urodzenia: 16.04.2000 Miejsce urodzenia: Lublin
JSON (skrót od JavaScript Object Notation) to bardzo popularny i poręczny format wymiany danych między systemami. Struktura informacji w JSONie jest bardzo podobna do słowników Pythona. Oto przykład danych zapisanych w formacie JSON:
{
"name":"Jan",
"surname":"Kiełbasa",
"data_of_birth":"12.12.2000",
"place_of_birth":"Białystok"
}
Python ma wbudowany pakiet json
, który ułatwia czytania i zapisywanie danych zapisanych w formacie JSON.
Możemy przekształcić słownik Pythona w strukturę formatu JSON za pomocą metody json.dump()
i zapisać ją do pliku:
dict_example = {
"name":"Jan",
"surname":"Kiełbasa",
"data_of_birth":"12.12.2000",
"place_of_birth":"Białystok"
}
with open("file.json", "w") as file:
json.dump(dict_example, file)
Poniżej pokazujemy przykład użycia metody json.load()
do wczytania danych w formacie JSON. Zauważcie, że wynikiem jest słownik.
import json
with open("file.json", "r") as file:
data = json.load(file)
print(data)
type(data)
{'name': 'Jan', 'surname': 'Kiełbasa', 'data_of_birth': '12.12.2000', 'place_of_birth': 'Białystok'}
dict
Python zawiera wiele funkcji wbudowanych. Do tej pory poznaliśmy już kilka z nich: str()
, print()
, input()
, czy range()
.
W Pythonie możemy pisać własne funkcje. Dzięki funkcjom możemy „zamknąć” część kodu programu w zwartej strukturze. Zobaczymy dalej, analizując kilka przykładów, że używanie własnych funkcji ma wiele korzyści i tak naprawdę trudno sobie wyobrazić programowanie Pythonie bez częstego korzystanie z własnych funkcji.
Każda dobrze zdefiniowana funkcja wygląda tak:
def NAZWA FUNKCJI(LISTA PARAMETRÓW):
"""
opis działania funkcji
"""
BLOK INSTRUKCJI
Definicja funkcji zaczyna się od słowa kluczowego def
po którym następuje NAZWA FUNKCJI
, po której znów mamy w nawiasach zwykłych LISTĘ PARAMETRÓW
funkcji oraz :
.
W kolejnych liniach, poprzedzony odpowiednią tabulacją, pojawia się komentarz zawierający opis działania funkcji, jej parametrów i ew. co funkcja zwraca oraz BLOK INSTRUKCJI
, który zostaje wykonany po wywołaniu funkcji. Komentarz może być pominięty, jeśli funkcja jest prosta, a jej działanie oczywiste.
Rozważmy poniższe dwa przykłady definicji funkcji:
def print_welcome_message(name, surname):
"""
print_welcome_message displays the welcome message.
:param name: name string
:param surname: surname string
"""
print(f'Witaj człowieku o imieniu {name} i nazwisku {surname}!')
Funkcja nazywa się print_welcome_message
i ma dwa parametry name
i surname
. Blok instrukcji tej funkcji składa się z jednej instrukcji print()
.
Wywołujemy funkcję podając jej nazwę oraz przekazując jej argumenty, które powinny pojawić się w miejscu odpowiednich parametrów:
print_welcome_message(name='Robert', surname='Trypuz')
Witaj człowieku o imieniu Robert i nazwisku Trypuz!
Jedną z zalet funkcji jest możliwość wielokornego ich wywoływania z różami argumentami jak to pokazujemy poniżej:
print_welcome_message('Maria','Zawada')
Witaj człowieku o imieniu Maria i nazwisku Zawada!
Gdy raz napiszemy dobrze funkcję, możemy ją w zasadzie traktować jako black box mający swoje wejście i wyjście. Sposób działania nie zawsze musi nam zaprzątać głowę.
Warto jeszcze dodać, że funkcje nie muszą mieć parametrów. Na przykład funkcja poniżej ma jedynie wyświetlić obrazek filiżanki z pachnącą kawą (przepisując ten kod możesz ten obrazek uprościć):
def espresso_please():
"""
espresso_please displays the cup picture.
"""
espresso = r'''
( )
) (
( )
_______
<_______> ___
| |/ _ \
| | | | |
| |_| |
___| |\___/
/ \_______/ \
\________________/
'''
print(espresso)
Zauważ r
przed napisem w powyższym kodzie. Nieco upraszczając można powiedzieć, że nakazuje on Pythonowi potraktować napis dokładnie tak jak my go widzimy.
espresso_please()
( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/
def more_espresso_please(number):
"""
more_espresso_please displays the cup picture number times.
:param number: number of times to display the cup picture
"""
for i in range(number):
espresso_please()
more_espresso_please(2)
( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/ ( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/
Pamiętamy, że zmienne w pętli nie były „widoczne” poza pętlą. Podobnie zmienne w pewnym bloku kodu nie były widoczne poza nim. Tak też będzie z funkcjami. Zmienne i argumenty wewnątrz funkcji mają zasięg lokalny (ang. local scope), dlatego nazywane są zmiennymi lokalnymi. Zmienne będące na zewnątrz wszystkich funkcji mają zasięg globalny (ang. global scope), stąd nazywane są zmiennymi globalnymi.
Istnieje tylko jeden zasięg globalny i jest on utworzony wraz z rozpoczęciem programu. Zasięg lokalny jest tworzony za każdym razem, gdy dowolna funkcja jest wywoływana. Zmienne z zasięgu globalnego są dostępne w zasięgu lokalnym, ale nie odwrotnie.
Wywołując funkcję możemy pominąć nazwy parametrów i od razu podać argumenty w nawiasie:
print_welcome_message('Robert', 'Trypuz')
Witaj człowieku o imieniu Robert i nazwisku Trypuz!
Są to tzw. argumenty pozycyjne i musimy pamiętać, że kolejność ich zapisania ma znaczenie. Porównaj:
print_welcome_message('Trypuz', 'Robert')
Witaj człowieku o imieniu Trypuz i nazwisku Robert!
print_welcome_message(surname='Trypuz', name='Robert')
Witaj człowieku o imieniu Robert i nazwisku Trypuz!
Python pozwala na przypisanie parametrom funkcji domyślnych wartości. Korzyść z tego jest taka, że wywołując tę funkcję możemy pominąć przekazanie jej niektórych argumentów, oczywiście o ile jesteśmy zadowoleni z ich wartości domyślnej. Poniżej definiujemy funkcję n_espresso_please
, która wyświetli obrazek filiżanki kawy number
razy, przy czym domyślnie 1.
def more_espresso_please(number=1):
"""
n_espresso_please displays the cup picture number times.
:param number: number of times to display the cup picture
"""
for i in range(number):
espresso_please()
more_espresso_please()
( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/
more_espresso_please(number=2)
( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/ ( ) ) ( ( ) _______ <_______> ___ | |/ _ \ | | | | | | |_| | ___| |\___/ / \_______/ \ \________________/
Podobnie funkcja poniżej wyświetla nazwę produkt oraz kraj jego pochodzenia. Domyślnym krajem jest Polska
.
def product_info(product_name, country_of_orgin='Polska'):
"""
product_info displays info about product, its name and country of orgin.
:param product_name: name of product
:param country_of_orgin: country of orgin string; 'Polska' by default
"""
print(f'Nazwa produktu: {product_name}, kraj pochodzenia: {country_of_orgin}')
product_info('kiełbasa')
product_info('kawa', 'Nigeria')
Nazwa produktu: kiełbasa, kraj pochodzenia: Polska Nazwa produktu: kawa, kraj pochodzenia: Nigeria
Wartości opcjonalne parametrów, czyli „mogą być, ale też może ich nie być”, to sposób na poszerzenie zakresu działania funkcji. Krótko mówiąc, dzięki wartościom opcjonalnym funkcje mogą więcej, ponieważ dostosowują się do różnych sytuacji. Jedna funkcja z wartościami opcjonalnymi może zastąpić kilka funkcji z wartościami wymaganymi. Poniższa funkcja „poszerza” zakres działania wcześniej zdefiniowanej funkcji o tej samej nazwie:
def print_welcome_message(name=None, surname=None):
"""
print_welcome_message displays the welcome message.
If name and surname are provided, the funciton refers to them.
:param name: name string, optional
:param surname: surname string, optional
"""
if name is None and surname is None:
print('Witaj!')
else:
print(f'Witaj człowieku o imieniu {name} i nazwisku {surname}!')
print_welcome_message()
Witaj!
print_welcome_message(name='Robert', surname='Trypuz')
Witaj człowieku o imieniu Robert i nazwisku Trypuz!
Funkcja nie musi nic wyświetlać. Zadaniem niektórych funkcji będzie zwracania wartości. Funkcja poniżej przyjmuje dwa parametry, a następnie zwraca wynik operacji +
.
def add(a, b):
"""
add function adds two numbers or concatenates two strings.
If name and surname are provided, the funciton refers to them.
:param a: number or string
:param b: number or string
:return: sum of two numbers or concatenation of two strings
"""
result = a + b
return result
add(2,3)
5
add(2,3) * add(3,4)
35
add('Robert', 'Trypuz')
'RobertTrypuz'
Parametrem funkcji mogą być różne struktury danych. Poniższa funkcja zakłada, że parametrem będzie lista osób:
def print_awart_winners(list_of_winners):
print('Oto nasi zwycięzcy:')
for winner in list_of_winners:
print('-', winner)
print_awart_winners(['Jan z Warszawy', 'Maria z Grudziądza', 'Janina z Sokółki'])
Oto nasi zwycięzcy: - Jan z Warszawy - Maria z Grudziądza - Janina z Sokółki
Funkcje mogą również zwracać różne struktury danych. Poniższa funkcja zwraca słownik utworzony z argumentów funkcji.
def create_student_dict(name, surname, date_of_birth, place_of_birth):
student_dict = {'name': name,
'surname': surname,
'date_of_birth': date_of_birth,
'place_of_birth': place_of_birth}
return student_dict
create_student_dict('Robert', 'Trypuz', '12.07.1956', 'Lublin')
{'name': 'Robert', 'surname': 'Trypuz', 'date_of_birth': '12.07.1956', 'place_of_birth': 'Lublin'}
students_list = []
students_list.append(create_student_dict('Jan', 'Kiełbasa', '12.12.2000', 'Białystok'))
students_list.append(create_student_dict('Monika', 'Szacka', '16.04.2000', 'Lublin'))
print(students_list)
[{'name': 'Jan', 'surname': 'Kiełbasa', 'date_of_birth': '12.12.2000', 'place_of_birth': 'Białystok'}, {'name': 'Monika', 'surname': 'Szacka', 'date_of_birth': '16.04.2000', 'place_of_birth': 'Lublin'}]
*languages
poniżej to krotka z dowolną liczbą argumentów.
def add_foreign_languages(*languages):
if len(languages) > 1:
print('Kandydat zna języki obce:')
for language in languages:
print(f'- {language}')
elif len(languages) == 1:
print(f'Kandydat zna język {languages[0]}.')
else:
print('Z języków obcych to kandyda ogarnia tylko te w swoich w butach.')
add_foreign_languages('angielski')
Kandydat zna język angielski.
add_foreign_languages('angielski', 'hiszpański')
Kandydat zna języki obce: - angielski - hiszpański
**languages
to słownik z dowolną liczbą elementów.
def add_foreign_languages(**languages):
if len(languages) > 1:
print('Kandydat zna języki obce:')
for language, level in languages.items():
print(f'- {language}: {level}')
elif len(languages) == 1:
print(f'Kandydat zna język {list(languages.keys())[0]} {languages[list(languages.keys())[0]]}.')
else:
print('Z języków obcych to kandyda ogarnia tylko te w swoich w butach.')
add_foreign_languages(angielski='biegle', włoski='początkująco')
Kandydat zna języki obce: - angielski: biegle - włoski: początkująco
Na koniec podsumujmy zalety używania funkcji oraz powiedzmy kilka słów o estetyce programowania funkcji.
Zalet używania funkcji:
Oto kilka wskazówek, które pozwolą ci poprawić styl twojego kodu:
_
=
.
Jeśli definiujesz kilka funkcji oddziel je dokładnie dwoma wierszami/liniami.Więcej informacji odnośnie stylu pisania kodu znajdziesz w PEP 8 – Style Guide for Python Code.
Programowanie obiektowe to paradygmat programowania, w którym używa się obiektów, zdefiniowanych za pomocą stanu, zapisanego w atrybutach, oraz zachowania (realizowane za pomocą metod). Często programy napisane w Pythonie są zbiorem takich obiektów.
Obiekty są tworzone na podstawie definicji zwanej klasą. Klasa zawiera informacje, jakie właściwości powinien posiadać każdy egzemplarz tej klasy. Na podstawie jednej klasy można stworzyć wiele obiektów.
Żeby zdefiniować klasę należy posłużyć się słowem kluczowym class
, po którym musi zostać podana nazwa klasy. Standardowo nazwy klas rozpoczyna się dużą literą. Jak wspomnieliśmy wcześniej, obiekty mają stan i zachowanie. Do definiowania zachowania służą metody. Metody to funkcje dostępne w ramach klasy. Definiuje się je za pomocą słowa kluczowego def
, a jako pierwszy parametr zawsze trzeba podać self
, co pozwala na używanie referencji do obiektu w ramach danej metody. Bardziej szczegółowy opis użycia parametru self
zostanie przedstawiony później.
W celu uzyskanie większej kontroli nad procesem tworzenia nowego obiektu, należy użyć konstruktora. Konstruktory to specjalne metody, które zostają wykonane zaraz po utworzeniu obiektu. Służą do nadania obiektom początkowego stanu. Żeby utworzyć konstruktor należy użyć specjalnej nazwy metody __init__
. Poniżej znajduje się klasa Student
z definicją konstruktora.
class Student():
def __init__(self, name, surname, id):
self.name = name
self.surname = surname
self.id = id
Oto egzemplarz klasy Student
:
student_1 = Student(name='Jan', surname='Kiełbasa', id=123456)
Atrybuty:
print(student_1.name)
print(student_1.surname)
print(student_1.id)
Jan Kiełbasa 123456
Dodajmy do klasy Student
dwie metody, które posłużą nam do zmiany wartości atrybutów:
class Student():
def __init__(self, name, surname, id):
self.name = name
self.surname = surname
self.id = id
def change_name(self, new_name):
self.name = new_name
def change_surname(self, new_surname):
self.surname = new_surname
student_1 = Student(name='Jan', surname='Kiełbasa', id=123456)
student_1.change_name('Robert')
print(student_1.name)
Robert
Następnei dodamy dwie kolejne metody get
:
class Student():
def __init__(self, name, surname, id):
self.name = name
self.surname = surname
self.id = id
def change_name(self, new_name):
self.name = new_name
def change_surname(self, new_surname):
self.surname = new_surname
def get_name(self):
print(f'Student(ka) ma na imię {self.name}.')
def get_surname(self):
print(f'Student(ka) ma na nazwisko {self.surname}.')
student_1 = Student(name='Jan', surname='Kiełbasa', id=123456)
student_2 = Student(name='Mariola', surname='Marchewka', id=789012)
student_1.get_name()
student_2.get_name()
student_2.get_surname()
student_2.change_surname('Pietrucha')
student_2.get_surname()
Student(ka) ma na imię Jan. Student(ka) ma na imię Mariola. Student(ka) ma na nazwisko Marchewka. Student(ka) ma na nazwisko Pietrucha.
Czasami istnieje potrzeba zdefiniowania jednej metody albo jednej wartości dla wszystkich obiektów danej klasy (a nie dla poszczególnych obiektów). Tego typu elementy nazywa się statycznymi. Możemy więc mówić o metodach statycznym oraz o polach statycznych. Żeby zdefiniować metodę statyczną, należy użyć dekoratora @staticmethod
. Statyczne atrybuty klasy definiuje się w treści klasy.
Poniżej znajduje się fragment nowej wersji definicji klasy Student z wykorzystaniem metody statycznej status oraz z atrybutem statycznym total
, które służą do zliczania liczby stworzonych obiektów klasy Student
.
class Student():
total = 0
def __init__(self, name, surname, id):
self.name = name
self.surname = surname
self.id = id
Student.total += 1
def change_name(self, new_name):
self.name = new_name
def change_surname(self, new_surname):
self.surname = new_surname
def get_name(self):
print(f'Student(ka) ma na imię {self.name}.')
def get_surname(self):
print(f'Student(ka) ma na nazwisko {self.surname}.')
@staticmethod
def status():
print(f'Liczba utworzonych obiektów klasy Student: {Student.total}.')
student_1 = Student(name='Jan', surname='Kiełbasa', id=123456)
student_2 = Student(name='Mariola', surname='Marchewka', id=789012)
Student.status()
Liczba utworzonych obiektów klasy Student: 2.
Dziedziczenie pozwala odwołać się do metod innej klas, uważanej za bardziej ogólną. To odwołanie nazywa się dziedziczeniem (ang. inheritance). Poniżej zdefiniujemy klasę StudentExternal
, która będzie dziedziczyć metody klasy Student
.
class StudentExternal(Student):
pass
student_external1 = StudentExternal('Nina', 'Kowalska', 4567896)
student_external1.get_name()
Student(ka) ma na imię Nina.
Dodamy do StudentExternal
jeden nowy parametr study_field
.
class StudentExternal(Student):
def __init__(self, name, surname, id, study_field):
super().__init__(name, surname, id)
self.study_field = study_field
student_external1 = StudentExternal('Nina', 'Kowalska', 4567896, 'AI')
student_external1.get_surname()
Student(ka) ma na nazwisko Kowalska.
student_external1.study_field
'AI'
Przeanalizuj poniższy kod. Klasa trójkątów równobocznych EquilateralTriangle
dziedziczy po klasie Triangle
.
class Triangle:
def __init__(self, edge1, edge2, edge3):
self.edge1 = edge1
self.edge2 = edge2
self.edge3 = edge3
def perimeter(self):
return self.edge1 + self.edge2 + self.edge3
class EquilateralTriangle(Triangle):
def __init__(self, edge):
super().__init__(edge, edge, edge)
triangle = Triangle(3, 4, 5)
print(f'Obwód trójkąta nierównobocznego: {triangle.perimeter()}.')
equilateral_triangle = EquilateralTriangle(3)
equilateral_triangle.perimeter()
print(f'Obwód trójkąta równobocznego: {equilateral_triangle.perimeter()}.')
Obwód trójkąta nierównobocznego: 12. Obwód trójkąta równobocznego: 9.
Atrybutem klasy może być obiekt, jak to demostruje poniższy przykład.
class StudyField:
def __init__(self, field_name, dean):
self.field_name = field_name
self.dean = dean
cog_science = StudyField('Cognitive Science', 'Marek Lechniak')
class StudentExternal(Student):
def __init__(self, name, surname, id, field_name, dean):
super().__init__(name, surname, id)
self.study_field = StudyField(field_name, dean)
student_external1 = StudentExternal('Nina', 'Kowalska', 4567896, 'Cognitive Science', 'Marek Lechniak')
student_external1.study_field.dean
'Marek Lechniak'
Nazwy klas powinny być w stylu PascalCase
, tj. każde słowo w nazwie klasy powinno być zaczynać się dużą literą, a między słowami nie powinno być spacji.
Nazwy obiektów (aka egzemplarzy) klas powinny stosować się do tych samych zasad co nazwy funkcji (czyli powinny składać się z małych liter, a słowa powinny być połączone znakiem _
).
Obiekty iterowalne to obiekty, po których można iterować na przykład za pomocą pętli for
. W Pythonie obiektami iterowalnymi są: napisy, listy, słowniki, zbiory, krotki oraz pliki. Liczby nie są. Zbadaj poniższy kod.
for letter in 'Ala ma kota':
print(letter)
A l a m a k o t a
for number in 234:
print(number)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Input In [373], in <cell line: 1>() ----> 1 for number in 234: 2 print(number) TypeError: 'int' object is not iterable
Druga z pętli for
zwróci błąd 'int' object is not iterable
.
Aby obiekt był iterowalny musi mieć zaimplementowany specjalny protokół, na który składają się:
__iter__
, która zwraca iterator i informuje Pythona, że obiekt jest iterowalny__next__
, która musi być zdefiniowana na iteratorze; jej zadaniem jest przechwytywanie i zwracanie kolejnych wartości albo zakończenie iterowania za pomogą wyjątku StopIteration
.Poniższa klasa MyIterator
wyjaśnia dziłanie iteratora.
class MyIterator():
def __init__(self, data):
print('[Konstruktor __init__ przyjmuje dane i inicjuje indeks.]')
self.data = data
self.index = 0
def __iter__(self):
print('[Metoda __iter__ informuje Pythona, że obiekt jest iterowalny.]')
return self
def __next__(self):
data_len = len(self.data)
if self.index >= data_len:
print(f'[Metoda __next__: StopIteration; nie ma indeksu {self.index}]')
raise StopIteration
value = self.data[self.index]
self.index += 1
print(f'[Metoda __next__: przechwytuje aktualną wartość "{value}" z indeksu {self.index-1}/{data_len-1} i przechodzi do indeksu {self.index}.]')
return value
my_iterator = MyIterator(['A', 'B', 'C'])
for item in my_iterator:
print(f'Zwracany wynik: {item}')
[Konstruktor __init__ przyjmuje dane i inicjuje indeks.] [Metoda __iter__ informuje Pythona, że obiekt jest iterowalny.] [Metoda __next__: przechwytuje aktualną wartość "A" z indeksu 0/2 i przechodzi do indeksu 1.] Zwracany wynik: A [Metoda __next__: przechwytuje aktualną wartość "B" z indeksu 1/2 i przechodzi do indeksu 2.] Zwracany wynik: B [Metoda __next__: przechwytuje aktualną wartość "C" z indeksu 2/2 i przechodzi do indeksu 3.] Zwracany wynik: C [Metoda __next__: StopIteration; nie ma indeksu 3]
Generator jest funkcją, która zwraca iterator. Generator wygląda jak zwyczaja funkcja i musi zawierać przynajmniej jedno wyrażenie yield
, którego zadaniem jest przechowywanie stanu w jakim się funkcja znajduje po wywołaniu i przy kolejnym wywołaniu kontynuowanie od tego właśnie miejsca.
Generatory automatycznie implementują __iter__
, __next__
i StopIteration
, więc nie musimy się tym sami zajmować.
Wywołanie generatora nie sprawia, że zaczyna się od razu wykonywać/iterować. Iterowanie odbywa się za pomocą next()
. Po jednej iteracji generatu pauzuje i czeka na kolejne wywołanie (oczywiście zapamiętując stan w jakim się znajduje).
def my_generator(my_list):
for i in range(len(my_list)):
yield my_list[i]
for item in my_generator(['A', 'B', 'C']):
print(f'Zwracany wynik: {item}')
Zwracany wynik: A Zwracany wynik: B Zwracany wynik: C
my_gen = my_generator(['A', 'B', 'C'])
print(next(my_gen))
print(next(my_gen))
print(next(my_gen))
print(next(my_gen))
A B C
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) Input In [399], in <cell line: 6>() 4 print(next(my_gen)) 5 print(next(my_gen)) ----> 6 print(next(my_gen)) StopIteration:
Generatory są bardzo pożyteczne, gdy chcemy uniknąć zaśmiecania pamięci komputera na przykład listami elementów, szczególnie, gdy te listy są bardzo duże. Poniższy kod pokazuje różnicę między listą my_list
, która tworzy są w całości i zajmuje miejsce w pamięci i generatorem my_generator
, który nie generuje od razu calej listy liczb od 0 do 4.
my_list = [x for x in range(5)]
my_generator = (x for x in range(5))
for i in my_generator:
print(i)
0 1 2 3 4