1.10. Extra Lotek
Program „Duży Lotek” omówiony w poprzednim przykładzie można podzielić na części kodu realizujące pojedyncze zadania, np. pobranie danych wejściowych, wylosowanie liczb, pobranie typów użytkownika, sprawdzenie i wypisanie danych wyjściowych. Realizację takich zadań możemy umieścić w funkcjach, których będzie można używać w innych programach.
Informacja
Przykład „Extra Lotek” pokazuje, jak używać funkcji i modułów oraz jak zapisywać i odczytywać dane z plików tekstowych w różnych formatach.
1.10.1. Zadanie
Napisz program extra_lotek.py, który losuje n liczb naturalnych
z podanego zakresu maks, a następnie pobiera z klawiatury n typów, sprawdza i wypisuje,
ile z nich zostało trafionych. Pobieranie typów, sprawdzanie i wypisywanie wyników
powtarza się tyle razy, ile poda użytkownik. Dodatkowo program powinien:
sprawdzać poprawność podawanych danych wejściowych,
zapisywać w pliku i odczytywać dane wejściowe: nick użytkownika, liczbę losowanych liczb, wartość maksymalną oraz liczbę typowań,
pozwalać na zmianę danych wejściowych.
Realizację kolejnych zadań zakoduj w funkcjach umieszczonych w module.
Dane:
nick– nick użytkownika, ciąg znaków pobierany z klawiatury,n– liczba naturalna pobierana z klawiatury większa odn,maks– liczba całkowita pobierana z klawiatury,ile_typowan– liczba całkowita pobierana z klawiatury,typ– liczba całkowita pobierana z klawiatury z zakresu<0; maks>.
Wynik
Komunikaty podczas pierwszego uruchomienia:
Podaj nick: adam
Podaj liczbę losowanych liczb: 3
Podaj maksymalną losowaną liczbę: 10
Podaj liczbę typowań: 1
Wytypuj 3 z 10 liczb:
Podaj typ: 1
Podaj typ: 3
Podaj typ: 5
Wylosowane liczby: [10, 3, 4]
Trafione liczby: {3}
Liczba trafień 1
Początkowe komunikaty po kolejnym uruchomieniu przez tego samego użytkownika:
Podaj nick: adam
Ustawienia:
Liczb: 3
Maks: 10
Typowań: 1
Zmieniasz (t/n)? n
Wytypuj 3 z 10 liczb:
...
1.10.2. Funkcja główna
Tam, gdzie w programie występują powtarzające się operacje lub zestaw poleceń realizujący wyodrębnione zadanie, wskazane jest używanie funkcji. Wyodrębnienie funkcji poprawia czytelność działania programu, ułatwia sprawdzanie i poprawianie kodu.
Jeżeli zadania realizowane przez program umieszczamy w funkcjach, to jedną z nich trzeba wywołać na początku,
aby program zaczął działać. Taka funkcja zwyczajowo nazywana jest main()
i zazwyczaj zawiera logikę działania programu, tj. wywołuje inne funkcje wykonujące osobne zadania.
W nowym pliku extra_lotek.py umieszczamy początkowy kod:
def main(args):
nick = input('Podaj nick: ')
pass
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
Funkcja w Pythonie to wyodrębniony i nazwany blok kodu.
Definicja funkcji def main(args): zawiera słowo kluczowe def,
nazwę funkcji z okrągłymi nawiasami, np. main(), opcjonalne parametry, np. args oraz dwukropek.
Instrukcja warunkowa if __name__ == '__main__': sprawdza, czy nasz plik został uruchomiony
bezpośrednio przez interpreter, który zmienną specjalną __name__ ustawia wtedy na __main__.
Jeżeli plik zostałby zaimportowany, zmienna __name__ ustawiona zostałaby na nazwę pliku.
Jeżeli plik wykonywany jest bezpośrednio, uruchamiana jest funkcja główna main(args) jako argument metody
sys.exit(), która kończy wykonywanie programu. Wartość zwracana przez funkcję główną informuje
o tym, jak zakończył się program, np. zero (0) oznacza, że program zakończył się poprawnie.
Informacja
W prostych programach pisanych na własny użytek można zrezygnować z bloku if __name__ == '__main__':,
a także usunąć argument args z definicji funkcji głównej. Trzeba jednak pamiętać o wywołaniu
funkcji głównej, tj. umieszczeniu instrukcji main() bez wcięcia po definicji funkcji głównej.
Instrukcja pass nie jest wykonywana, to informacja, że w danym miejscu zostanie wstawiony jakiś kod.
Pozwala również uniknąć błędu w miejscach, w których brak kodu jest niedozwolony, np.
po instrukcji warunkowej lub pętli.
1.10.3. Moduł
Często używane funkcje umieszczamy w osobnych modułach (zob. moduł), z których
importujemy je do różnych programów za pomocą instrukcji import lub from ... import.
Informacja
Jeżeli program korzysta z niewielu i/lub unikalnych funkcji, możemy umieścić je na początku programu w jednym pliku.
1.10.3.1. Wczytywanie ustawień
W katalogu ze skryptem naszego programu tworzymy nowy plik modul_lotek.py,
dodajemy importy modułów wbudowanych oraz funkcji wczytującej zapisanie ustawienia:
1import random
2import os
3import json
4
5
6def wczytaj_ustawienia(nick):
7 nazwa_pliku = nick + '.txt'
8 if os.path.isfile(nazwa_pliku):
9 with open(nazwa_pliku) as plik:
10 wiersz = plik.readline()
11 dane = wiersz.strip().split(';')
12 return dane
13 return False
14
Funkcja wczytaj_ustawienia() otrzymuje parametr nick, który wykorzystujemy do zdefiniowania nazwy pliku
z ustawieniami użytkownika. Za pomocą metody isfile() sprawdzamy, czy plik znajduje się
w ścieżce przeszukiwania, czyli w praktyce w katalogu naszego programu głównego. Jeżeli tak, otwieramy go
w domyślnym trybie „do odczytu” – with open(nazwa_pliku) as plik: – i udostępniamy w zmiennej plik.
Plik zawiera jeden wiersz w formacie nick;liczba losowanych liczb;maksymalna liczba;liczba typowań.
Do jego odczytania używamy metody readline(). Z otrzymanego ciągu znaków usuwamy znaki końca wiersza
przy użyciu metody strip(). Następnie dzielimy tekst za pomocą metody split() i znaku średnika na części,
które zwracane są w postaci listy dane.
1.10.3.2. Pobieranie ustawień
Do naszego modułu dodajemy kolejną funkcję o nazwie pobierz_ustawienia(), której zadaniem
będzie pobieranie danych wejściowych z klawiatury:
16def pobierz_ustawienia(nick):
17 error = False
18 while True:
19 try:
20 n = int(input('Podaj liczbę losowanych liczb: '))
21 maks = int(input('Podaj maksymalną losowaną liczbę: '))
22 ile_typowan = int(input('Podaj liczbę typowań: '))
23 if n >= maks or ile_typowan < 1:
24 error = True
25 except ValueError:
26 error = True
27
28 if error:
29 print("Błędne dane!")
30 continue # dane niepoprawne
31 else:
32 break # dane poprawne
33
34 dane = [nick, n, maks, ile_typowan]
35 return dane
36
Funkcja pobierz_ustawienia() wykorzystuje nieskończoną pętlę warunkową while True,
w której pobierane są kolejne dane wejściowe. W przypadku podania błędnych danych pętla zaczyna działanie od początku
dzięki instrukcji continue, jeżeli dane są poprawne przerywamy działanie pętli instrukcją
break i zwracamy je w postaci listy dane.
Informacja
Wyjaśnienie instrukcji try ... except znajdziesz w materiale Duży lotek
w sekcji „Poprawność danych”.
1.10.3.3. Zapisywanie ustawień
Dodajemy do modułu funkcję zapisz_ustawienia(), która pobierać będzie dwa parametry
zawierające nick użytkownika i dane do zapisania.
38def zapisz_ustawienia(nick, dane):
39 nazwa_pliku = nick + '.txt'
40 with open(nazwa_pliku, 'w') as plik:
41 plik.write(';'.join(dane) + '\n')
42
Początek funkcji jest prawie identyczny jak w funkcji wczytaj_ustawienia() z tą tylko różnicą,
że plik otwierany jest w trybie do zapisu, tj, podajemy drugi argument w funkcji open() – znak w.
W efekcie, jeżeli pliku nie ma na dysku – zostanie zapisany, jeżeli jest – zostanie nadpisany.
Parametr dane jest 4-elementową listą zawierająca pobrane od użytkownika lub odczytane z dysku
dane wejściowe. Dane te łączymy za pomocą metody join() i znaku średnika. Otrzymany ciąg znaków
uzupełniamy znakiem końca wiersza \n i zapisujemy w pliku za pomocą metody .write().
1.10.4. Dane wejściowe
Wykorzystajmy przygotowane funkcje w programie głównym. Na początku pliku extra_lotek.py
importujemy dodane funkcje:
1from modul_lotek import wczytaj_ustawienia, pobierz_ustawienia, zapisz_ustawienia
Następnie instrukcję pass zastępujemy poniższym kodem:
7 dane = wczytaj_ustawienia(nick)
8 print(dane) # tę instrukcję można zakomentować
9 if dane:
10 print(f'Ustawienia:\nLiczb: {dane[1]}\nMaks: {dane[2]}\nTypowań: {dane[3]}')
11
12 if not dane or input('Zmieniasz (t/n)? ').lower() == 't':
13 dane = pobierz_ustawienia(nick)
14 zapisz_ustawienia(nick, dane)
15
16 mick, n, maks, ile_typowan = dane[0:1] + [int(x) for x in dane[1:4]]
17 print(mick, n, maks, ile_typowan) # tę instrukcję można zakomentować
18
Po pobraniu nicka z klawiatury wywołujemy funkcję wczytaj_ustawienia(), która zwróci listę
z odczytanymi danymi lub wartość False. W instrukcji warunkowej, jeżeli lista nie jest pusta
(if dane:), wypisujemy dane oprócz nicka.
Jeżeli nie udało się odczytać danych (if not dane) lub jeżeli użytkownik chce zmienić odczytane dane
(or input('Zmieniasz (t/n)? ').lower() == 't'), wywołujemy funkcję pobierz_dane(), a następnie
funkcję zapisz_ustawienia(), która zapisuje listę dane zawierającą nick i pozostałe informacje.
W kolejnej instrukcji przypisujemy elementy listy dane do osobnych zmiennych.
Do wskazania poszczególnych elementów listy używamy notacji wycinkowej.
Instrukcja [int(x) for x in dane[1:4]] to wyrażenie listowe. Można je rozumieć
jako skrócony zapis pętli for, która odczytuje trzy ostatnie elementy listy dane[1:4],
zamienia je na liczby całkowite int(x) i tworzy nową listę. Jej elementy to liczby
przypisywane do zmiennych m, maks i ile_typowan.
1.10.5. Losowanie i sprawdzanie
Funkcje, które losują liczby do odgadnięcia, pobierają typy użytkownika, sprawdzają i wypisują wyniki
umieszczamy w module modul_lotek.py:
44def losuj_liczby(n, maks):
45 """Funkcja losuje i zwraca listę n unikalnych liczb całkowitych w zakresie <0;maks>"""
46 liczby = []
47 while len(liczby) < n:
48 liczba = random.randint(0, maks)
49 if liczby.count(liczba) == 0:
50 liczby.append(liczba)
51 return liczby
52
53
54def pobierz_typy(n, maks):
55 """Funkcja pobiera od użytkownika typy wylosowanych liczb"""
56 print(f'Wytypuj {n} z {maks} liczb: ')
57 typy = set()
58 error = False
59 while len(typy) < n:
60 try:
61 typ = int(input('Podaj typ: '))
62 if typ < 0 or typ > maks or typ in typy:
63 error = True
64 except ValueError:
65 error = True
66
67 if error:
68 print('Błędne dane!')
69 continue
70 typy.add(typ)
71 return typy
72
73def wypisz_wyniki(liczby, typy):
74 print()
75 trafione = set(liczby) & typy
76 if trafione:
77 print(f'Trafione liczby: {trafione}')
78 print(f'Liczba trafień {len(trafione)}')
79 else:
80 print('Brak trafień. Spróbuj jeszcze raz!')
81
82 print()
83 return len(trafione)
84
Dodane instrukcje to kod omówiony w scenariuszu Duży lotek przekształcony
na funkcje. Warto zwrócić uwagę, że zwracają one wartości, tj. listę wylosowanych liczb liczby,
zbiór typów użytkownika typy oraz liczbę trafień len(trafione).
Następnie w programie głównym uzupełniamy importy na początku pliku:
1from modul_lotek import wczytaj_ustawienia, pobierz_ustawienia, zapisz_ustawienia
2from modul_lotek import losuj_liczby, pobierz_typy, wypisz_wyniki
Następnie dopisujemy kod w funkcji głównej przed instrukcją return 0:
21 liczby = losuj_liczby(n, maks)
22
23 for i in range(ile_typowan):
24 typy = pobierz_typy(n, maks)
25 ile_trafionych = wypisz_wyniki(liczby, typy)
26 if ile_trafionych == n:
27 break
28
29 print('Wylosowane liczby:', liczby)
30
Po wylosowaniu liczb wykorzystujemy pętlę for i in range(ile_typowan), aby użytkownik mógł kilka razy typować liczby.
Wewnątrz pętli pobieramy typy, sprawdzamy i wypisujemy wyniki. Jeżeli liczba odgadniętych liczb,
czyli wartość zwrócona przez funkcję wypisz_wyniki() równa jest liczbie losowanych liczb, kończymy
pętlę instrukcją break.
1.10.6. Historia typowań
Skoro umiemy już zapamiętywać wstępne ustawienia programu, możemy również zapamiętywać losowania i typowania użytkownika, tworząc rejestr do celów informacyjnych i/lub statystycznych. Przyjmijmy, że będziemy zapisywali dane ostatniego typowania i będą one zawierały:
czas– data typowania,n– liczba losowanych liczb,maks– maksymalna losowana liczba,ile_typowan– liczba typowań,ustawienia– krotka z liczbą losowanych liczb, liczbą maksymalną i liczbą typowań,wylosowane– krotka z wylosowanymi liczbami,ile_trafionych– liczba trafionych liczb.
Do przechowywania tych informacji wykorzystamy słownik, w którym kluczami będą nazwy danych.
Zaczniemy od dodania dwóch funkcji w pliku modułu:
86def wczytaj_dane(nick):
87 """Funkcja odczytuje dane w formacie json z pliku"""
88 nazwa_pliku = nick + '.json'
89 dane = []
90 if os.path.isfile(nazwa_pliku):
91 with open(nazwa_pliku, 'r') as plik:
92 dane = json.load(plik)
93 return dane
94
95
96def zapisz_dane(nick, dane):
97 """Funkcja zapisuje dane w formacie json do pliku"""
98 nazwa_pliku = nick + '.json'
99 with open(nazwa_pliku, 'w') as plik:
100 json.dump(dane, plik)
Kiedy czytamy i zapisujemy dane w plikach, ważną sprawą staje się ich format. Najprościej zapisywać dane jako znaki, tak jak zrobiliśmy to z ustawieniami, jednak często programy użytkowe potrzebują zapisywać złożone struktury danych, np. listy, zbiory czy słowniki. Znakowy zapis wymagałby wtedy wielu dodatkowych manipulacji, aby możliwe było poprawne odtworzenie informacji. Prościej jest skorzystać z serializacji, czyli zapisu danych obiektowych (zob. serializacja). Często stosowany jest wtedy prosty format tekstowy JSON.
W funkcji wczytaj_dane() zawartość pliku o podanej nazwie dekoduje do listy: dane = json.load(plik),
która jest zwracana. Funkcja zapisz_dane() otwiera plik o podanej nazwie w trybie do zapisu
i zapisuje podaną listę danych w formacie JSON: json.dump(dane, plik). Ewentualne dane znajdujące się
w pliku są nadpisywane.
Pozostaje nam wykorzystać przygotowane funkcje w programie głównym. Na początku dodajemy importy:
1from modul_lotek import wczytaj_ustawienia, pobierz_ustawienia, zapisz_ustawienia
2from modul_lotek import losuj_liczby, pobierz_typy, wypisz_wyniki
3from modul_lotek import wczytaj_dane, zapisz_dane
Dalej na końcu funkcji main(), ale przed instrukcją return 0 dopisujemy:
31 losowania = wczytaj_dane(nick)
32 losowanie = {
33 'czas': datetime.strftime(datetime.now(), '%d.%m.%Y %H:%M:%S'),
34 'n': n,
35 'maks': maks,
36 'ile_typowan': ile_typowan,
37 'wylosowane': tuple(liczby),
38 'trafionych': ile_trafionych
39 }
40 losowania.append(losowanie)
41 zapisz_dane(nick, losowania)
42 print(losowania) # tę instrukcję można zakomentować
Do zmiennej losowania przypisujemy listę zwróconą przez funkcję wczytaj_dane().
Następnie tworzymy słownik losowanie, zawierający wszystkie omówione wyżej dane aktualnej „gry”.
Na uwagę zasługuje obsługa daty i czasu przy użyciu metod z podmodułu datetime:
now()– metoda zwraca obiekt reprezentujący aktualną datę i czas,strftime()– metoda pozwala przekształcić obiekt w ciąg znaków według ciągu formatującego%d.%m.%Y %H:%M:%S, który oznacza zapis w postacidzień.miesiąc.rok godzina:minuta:sekunda, np.:21.10.2025 16:07:23.
Informacja
Serializacja JSON nie obsługuje obiektów typu datetime, dlatego trzeba je zamieniać
na ciąg znaków. Alternatywnie do pokazanego wyżej sposobu można użyć metody zwracającej
datę w formacie ISO 8601, np. datetime.now().isoformat().
Przygotowany słownik dodajemy do listy słowników: losowania.append(losowanie),
którą wypisujemy w celach poglądowych ;-) i zapisujemy za pomocą funkcji zapisz_dane().
1.10.7. Ćwiczenia
Przećwicz w trybie interaktywnym notację wycinkową, wyrażenia listowe i łączenie list:
~$ python3
>>> dane = ['a', 'b', 'c', '1', '2', '3']
>>> dane[0:3]
>>> dane[3:6]
>>> duze = [x.upper() for x in dane[0:3]]
>>> kwadraty = [int(x)**2 for x in dane[3:6]]
>>> duze + kwadraty
Sprawdź w trybie interaktywnym, jak wygląda znakowa reprezentacja aktualnej daty i czasu w formacie ISO 8601.
Załóżmy, że jednak chcielibyśmy zapisywać historię losowań w pliku tekstowym, którego poszczególne linie zawierałyby dane jednego losowania, np.:
czas:2025-10-28T15:31:27.047438;n:2;maks:10;ile_typowan:2;wylosowane:(5, 7);trafionych:2Funkcja zapisująca dane mogłaby wyglądać np. tak:
Kod nrdef zapisz_txt(nick, dane): """Funkcja zapisuje dane w formacie txt do pliku""" nazwa_pliku = nick + "_losowania.txt" with open(nazwapliku, "w") as plik: for slownik in dane: wiersz = [k + ":" + str(w) for k, w in slownik.iteritems()] wiersz = ";".join(wiersz) print >>plik, wiersz + '\n'
Napisz funkcję
czytaj_txt()odczytującą tak zapisane dane, która zwraca listę słowników.Użyj obydwu funkcji w programie
extra_lotek.pytak, aby wypisywane były dane zwracane przez funkcjęzapisz_txt().Wskazówka
Nie musisz zastępować funkcji zapisujących dane w formacie JSON.
Pojęcia
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
- Utworzony:
2026-05-30 o 19:12 w Sphinx 7.3.7
- Autorzy: