Języki skryptowe - Python

Wykład 8


  • Błędy i wyjątki
  • Operacje na plikach

Błędy (bug)


Without requirements or design, programming is the art of adding bugs to an empty text file.

Louis Srygley

  • błędy leksykalne i składniowe
  • błędy typowania
  • błędy semantyczne i logiczne
  • błędy działania
  • nieskończone obliczenia

Błędy leksykalne


  • pojedyncza jednostka leksykalna, której nie przewiduje definicja języka
x = 1
x++ # operator ++ nie istnieje
  File "<ipython-input-1-c2b85b799f03>", line 2
    x++ # operator ++ nie istnieje
                                  ^
SyntaxError: invalid syntax

Błędy składniowe (syntax error)


  • niepoprawnie zestawione poprawne jednostki leksykalne
if True                   # brakuje :
    print("Hello World")) # dodatkowy )
  File "<ipython-input-2-3cc3ffe6806b>", line 1
    if True                   # brakuje :
                                         ^
SyntaxError: invalid syntax

Błędy typowania


  • wyrażenie nieadekwatne do typu
x = 1

x[0] = 2 # x nie jest sekwencyjnym typem danych
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-3-4bf6b631916d> in <module>()
      1 x = 1
      2 
----> 3 x[0] = 2 # x nie jest sekwencyjnym typem danych


TypeError: 'int' object does not support item assignment

Błędy działania (runtime error)


  • pojawiają się w trakcie działania programu (np. odczyt z pliku, który nie istnieje)
def iloraz(a, b):
    """Zwraca a / b"""
    return a / b

iloraz(10, 0) # dzielenie przez 0
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-4-976842d0fc36> in <module>()
      3     return a / b
      4 
----> 5 iloraz(10, 0) # dzielenie przez 0


<ipython-input-4-976842d0fc36> in iloraz(a, b)
      1 def iloraz(a, b):
      2     """Zwraca a / b"""
----> 3     return a / b
      4 
      5 iloraz(10, 0) # dzielenie przez 0


ZeroDivisionError: division by zero

Błędy semantyczne (semantic error)


  • niezgodność oczekiwań ze stanem faktycznym
def dzialanie(a, b, c):
    """Zwraca iloraz a przez sumę b i c."""
    return a / b + c # zamiast a / (b + c)

Błędy logiczne


  • program liczy nie to co trzeba (w tym też błędy semantyczne)
  • najtrudniejsze do znalezienia
def delta(a, b, c):
    """Liczy wyróżnik trójmianu kwadratowego."""
    return b - 4*a*c # zamiast b*b - 4*a*c

Nieskończone pętle


def loop(i = 0):
    while i < 10:
        i -= 1 # i zawsze będzie mniejsze od 10

Najdroższy myślnik w historii


  • NASA (1962); Mariner 1: Floryda -> Wenus

Review Board determined that the omission of a hyphen in coded computer instructions in the data-editing program allowed transmission of incorrect guidance signals to the spacecraft.

źródło

Therac-25


  • maszyna do radioterapii nowotworów
  • na skutek błędów programistycznych kilka osób zmarło na skutek napromieniowania
  • błąd typu race condition - przy zbyt szybkim wprowadzaniu danych (przez operatora) parametry zabiegu nie były prawidłowo inicjowane

Zapobieganie błędom


  • pisanie czytelnego kodu
  • code review
  • debugowanie

Czytelność kodu


  • zrozumiałe nazwy zmiennych (nawet kosztem długości)
  • komentowanie kodu źródłowego, który nie jest zrozumiały od razu
  • tworzenie dokumentacji w trakcie pisania programu
  • opisywanie przyjętych założeń (w komentarzach i/lub dokumentacji)

Code review


  • sprawdzenie kodu przez inną osobę

Debugowanie


If debugging is the process of removing software bugs, then programming must be the process of putting them in.

Edsger Dijkstra

  • systematyczne redukowanie błędów w kodzie
  • kontrolowane wykonanie programu
  • debugger

__debug__


%%writefile debug.py

# __debug__ - wbudowana stała
# równa True - jeśli uruchomione bez -O (optimize)

if __debug__:
    print("Jestem w trybie debugowania.")
else:
    print("Jestem w trybie normalnym.")
Overwriting debug.py
%%bash
python debug.py
Jestem w trybie debugowania.
%%bash
python -O debug.py
Jestem w trybie normalnym.

Przykład


%%writefile fib.py
"""Wyznacza pierwsze wyrazy ciągu Fibonacciego."""

fib = [0, 1]

for i in range(10):
    if not __debug__:
        print("i =", i)
        print("fib =", fib)
        print("fib[i-2] =", fib[i-2])
        print("fib[i-1] =", fib[i-1])
        print()

    fib.append(fib[i-2] + fib[i-1])

print(fib) # [0, 1, 1, 2, 3, 5, 8, 13, ...]
Overwriting fib.py
%%bash
python fib.py
[0, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 5]

Przykład - debug mode


%%bash
python -O fib.py
i = 0
fib = [0, 1]
fib[i-2] = 0
fib[i-1] = 1

i = 1
fib = [0, 1, 1]
fib[i-2] = 1
fib[i-1] = 0

i = 2
fib = [0, 1, 1, 1]
fib[i-2] = 0
fib[i-1] = 1

i = 3
fib = [0, 1, 1, 1, 1]
fib[i-2] = 1
fib[i-1] = 1

i = 4
fib = [0, 1, 1, 1, 1, 2]
fib[i-2] = 1
fib[i-1] = 1

i = 5
fib = [0, 1, 1, 1, 1, 2, 2]
fib[i-2] = 1
fib[i-1] = 1

i = 6
fib = [0, 1, 1, 1, 1, 2, 2, 2]
fib[i-2] = 1
fib[i-1] = 2

i = 7
fib = [0, 1, 1, 1, 1, 2, 2, 2, 3]
fib[i-2] = 2
fib[i-1] = 2

i = 8
fib = [0, 1, 1, 1, 1, 2, 2, 2, 3, 4]
fib[i-2] = 2
fib[i-1] = 2

i = 9
fib = [0, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4]
fib[i-2] = 2
fib[i-1] = 3

[0, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 5]

Wbudowany debugger


%%writefile fib.py

import pdb # wbudowany debugger

pdb.set_trace() # zacznij debugować

fib = [0, 1]

for i in range(10):
    fib.append(fib[i-2] + fib[i-1])

print(fib) # [0, 1, 1, 2, 3, 5, 8, 13, ...]
Overwriting fib.py

pdb


  • n - następna instrukcja
  • s - wejdź w funkcję
  • r - wyjdź z funkcji
  • p - wydrukuj zmienną
  • q - przerwij
  • enter - powtórz ostatnią komendę
  • ...

Wyjątki (exceptions)


  • błędy działania (runtime error)
  • wykryte podczas wykonywania są nazywane wyjątkami
  • programista może decydować co robić z wyjątkami

Przykład - iloraz


def iloraz(a, b):
    """Zwraca a / b"""
    return a / b
iloraz(10, 2)
5.0
iloraz(10, 0)
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-17-0a93c896245e> in <module>()
----> 1 iloraz(10, 0)


<ipython-input-15-a5a78f530c06> in iloraz(a, b)
      1 def iloraz(a, b):
      2     """Zwraca a / b"""
----> 3     return a / b


ZeroDivisionError: division by zero

Iloraz - zabezpiecznie if


def iloraz(a, b):
    """Zwraca a / b lub zero, jeśli b = 0."""
    if b == 0: # można tak, ale lepiej korzystać z wyjątków
        return 0
    return a / b
iloraz(10, 2)
5.0
iloraz(10, 0)
0

Iloraz - wyjątek


def iloraz(a, b):
    """Zwraca a / b lub zero, jeśli b = 0."""
    try:    # spróbuj
        return a / b
    except: # jeśli error to
        return 0
iloraz(10, 2)
5.0
iloraz(10, 0)
0

Przykład - pobierz liczbę całkowitą


while True:
    try:    # spróbuj rzutować na int
        x = int(input("Podaj liczbę: "))
        break
    except: # jeśli się nie uda to
        print("Spróbuj jeszcze raz.")
Podaj liczbę: a
Spróbuj jeszcze raz.
Podaj liczbę: 1.0
Spróbuj jeszcze raz.
Podaj liczbę: 1

Typy wyjątków


  • pełna lista wbudowanych wyjątków link
while True:
    try:
        x = int(input("Podaj liczbę: "))
        break
    except ValueError: # jeśli błąd wartości
        print("Spróbuj jeszcze raz.")
Podaj liczbę: a
Spróbuj jeszcze raz.
Podaj liczbę: 1.0
Spróbuj jeszcze raz.
Podaj liczbę: 1

Komunikat wyjątku


while True:
    try:
        x = int(input("Podaj liczbę: "))
        break
    except ValueError as err: # err = komunikat błędu
        print("Spróbuj jeszcze raz, bo", err)
Podaj liczbę: a
Spróbuj jeszcze raz, bo invalid literal for int() with base 10: 'a'
Podaj liczbę: 1.0
Spróbuj jeszcze raz, bo invalid literal for int() with base 10: '1.0'
Podaj liczbę: 1

Przykład


# więcej o plikach za chwilę

import sys

def read_data():
    try:
        plik = open('data.txt') # otwórz plik
        linia = plik.readline() # wczytaj linię
        dane = int(linia)       # rzutuj linię na int
    except IOError as err:      # IOError
        print("Błąd I/O:", err)
    except ValueError as err:   # ValueError
        print("Złe dane:", err)
    except:                     # InnyError
        print("Coś poszło nie tak...")
        sys.exit(0)

Przykład - test


read_data()
Błąd I/O: [Errno 2] No such file or directory: 'data.txt'
%%writefile data.txt
Python
Writing data.txt
read_data()
Złe dane: invalid literal for int() with base 10: 'Python'

try...finally


def iloraz(a, b):
    """Zwraca a / b."""
    try:
        return a / b
    finally: # wykonaj mimo zgłoszonego wyjątku
        print("Posprzątam bez względu na wyjątki.")
iloraz(10, 5)
Posprzątam bez względu na wyjątki.





2.0
iloraz(10, 0)
Posprzątam bez względu na wyjątki.



---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-33-0a93c896245e> in <module>()
----> 1 iloraz(10, 0)


<ipython-input-31-2179acde9974> in iloraz(a, b)
      2     """Zwraca a / b."""
      3     try:
----> 4         return a / b
      5     finally: # wykonaj mimo zgłoszonego wyjątku
      6         print("Posprzątam bez względu na wyjątki.")


ZeroDivisionError: division by zero

try...except...finally


def iloraz(a, b):
    """Zwraca a / b lub zero, jeśli b = 0."""
    try:
        return a / b
    except:
        print("Użytkownik nie zna matematyki.")
    finally: # wykona zawsze bez względu na wyni try
        print("Posprzątam bez względu na wyjątki.")
iloraz(10, 5)
Posprzątam bez względu na wyjątki.





2.0
iloraz(10, 0)
Użytkownik nie zna matematyki.
Posprzątam bez względu na wyjątki.

Zgłaszanie wyjątków


def iloraz(a, b):
    """Zwraca a / b."""
    if b == 0:
        raise NameError("Dzielenie przez zero.")
    return a / b
iloraz(10, 5)
2.0
iloraz(10, 0)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-39-0a93c896245e> in <module>()
----> 1 iloraz(10, 0)


<ipython-input-37-0c4a0fa13db9> in iloraz(a, b)
      2     """Zwraca a / b."""
      3     if b == 0:
----> 4         raise NameError("Dzielenie przez zero.")
      5     return a / b


NameError: Dzielenie przez zero.

Zgłaszanie wyjątków test


try:
    iloraz(10, 0)
except NameError as err:
    print("Błąd:", err)
Błąd: Dzielenie przez zero.

Własne wyjątki


  • możliwe jest definiowanie własnych wyjątków
  • o tym w przyszłości, jak już poznamy klasy

assert


# assert expression jest równoważne
if __debug__:
    if not expression: raise AssertionError
%%writefile assert.py

import sys

assert sys.argv[1] != "Python"

print("Assert test.")
Overwriting assert.py

assert - test


%%bash
python assert.py 1
Assert test.
%%bash
python assert.py Python
Traceback (most recent call last):
  File "assert.py", line 4, in <module>
    assert sys.argv[1] != "Python"
AssertionError
%%bash
python -O assert.py Python
Assert test.

Operacje na plikach


  • do otwierania plików służy funkcja wbudowana open
  • przyjmuje wiele argumentów, przy czym dwa najważniejsze to: file i mode
open(file, mode)
  • file - nazwa pliku (lub pełna ścieżka, jeśli nie w katalogu roboczym)
  • mode - tryb

Tryby pracy nad plikiem


Tryb Opis
r tylko do oczytu (domyślnie)
w tylko do zapisu (istniejący plik zostanie nadpisany)
x tylko do zapisu (plik nie może istnieć)
a tylko do zapisu (od końca pliku)
+ aktualizowanie pliku (odczyt i zapis)
t tryb tekstowy (domyślnie)
b tryb binarny
  • np. open(file, r+b) otwiera plik do odczytu, z możliwością zapisu, w trybie binarnym

Zapis do pliku


# otwórz plik do zapisu
# usuń zawartość jeśli plik istnieje
file = open("test", 'w')

file.write("0123456789") # zapisz do pliku

file.close() # zamknij plik
file.write("jeszcze coś") # plik już jest zamknięty
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-46-456f9e28996a> in <module>()
----> 1 file.write("jeszcze coś") # plik już jest zamknięty


ValueError: I/O operation on closed file.

Dopisywanie do pliku


# otwórz plik do zapisu
# ustaw się na końcu pliku
file = open("test", 'a')

file.write("abcdefghij") # zapisz do pliku

file.close() # zamknij plik

Odczyt pliku


file = open("test", "r") # otwórz tylko do odczytu

zawartosc = file.read() # wczytaj całą zawartość pliku

file.close()

print(zawartosc)
0123456789abcdefghij

r+ vs a


# a (append) - zaczyna dopisywać na koniec pliku
# r+ - zaczyna zapisywać od początku (nadpisując dane)

file = open("test", "r+") # odczyt z możliwością zapisu

file.write("." * 5) # zapisz 5 kropek

file.close()
%%bash
cat test
.....56789abcdefghij

w+ vs r+


# r+ - zaczyna zapisywać od początku (nadpisując dane)
# w+ - najpierw czyści plik (jeśli istnieje)

file = open("test", "w+") # zapis z możliwością odczytu

file.write("." * 5) # zapisz 5 kropek

file.close()
%%bash
cat test
.....

w vs w+


file = open("test", "w") # tylko zapis

file.write("." * 5) # zapisz 5 kropek

zawartosc = file.read()

file.close()

print(zawartosc)
---------------------------------------------------------------------------

UnsupportedOperation                      Traceback (most recent call last)

<ipython-input-53-6fb740cd3fea> in <module>()
      3 file.write("." * 5) # zapisz 5 kropek
      4 
----> 5 zawartosc = file.read()
      6 
      7 file.close()


UnsupportedOperation: not readable

w vs w+


file = open("test", "w+") # zapis z możliwościa odczytu

file.write("." * 5) # zapisz 5 kropek

zawartosc = file.read()

file.close()

print(zawartosc) # nie wydrukuje bo jesteśmy na końcu pliku

seek


file = open("test", "w+") # zapis z możliwościa odczytu

file.write("." * 5) # zapisz 5 kropek

file.seek(0) # ustaw położenie

zawartosc = file.read()

file.close()

print(zawartosc) # nie wydrukuje bo jesteśmy na końcu pliku
.....

tell


%%bash
cat test
.....
file = open("test", "a+") # dodawanie z możliwością odczytu

file.tell() # pozycja w pliku
5
file.write("12345") # dopisz 12345

file.tell()
10
%%bash
cat test
.....12345

seek and read


%%bash
cat test
.....12345
# seek(offset, punkt_odniesienia)
# po = 0, 1, 2 (początek pliku, bieżąca pozycja, koniec pliku)
# w trybie tekstowym tylko 0 jest dozwolone

file.seek(6, 0) # o 6 znaków od początku 

file.read(1)
'2'
file.tell() # read(n) przesuwa o n
7

Uwaga


  • wygodnie jest wczytać cały plik do pamięci
    • read() - zawartość jako pojedynczy string
    • readlines() - zawartość jako lista (linia -> element)
  • jednak w przypadku dużych plików może to byś katastrofalne, wtedy lepiej
    • read(n) - wczytaj n bajtów
    • readline() - wczytaj linię

Otwieranie plików a wyjątki


try:
    file = open("złe_dane")
    data = file.read() # zgłasza wyjątek
finally: # porządek nawet w przypadku wyjątku
    print("Czyszczę śmieci.")
    file.close()

with statement


  • gwarantuje, że jeśli wywołane zostało __enter__() (np. otwarcie pliku)
  • to zostanie wywołane __exit()__ (np. zamknięcie pliku)
  • nawet jeśli po drodze wystąpi wyjątek

with open


with open("plik_z_danymi") as file:
    data = file.read()
  • gwarantuje, że plik zostanie zawsze poprawnie zamknięty
  • wygodniejsze niż try...finally...

Comma-separated values (CSV)


  • forma przechowywania danych w plikach tesktowych
  • każde pole oddzielone jest przecinkiem
  • plik csv
imię, nazwisko, ocena
Kasia, Kowalska, 4
Jan, Nowak, 4
  • tabela
imię nazwisko ocena
Kasia Kowalska 4
Jan Nowak 4

Moduł csv


import csv

data = [["imie", "nazwisko", "ocena"],
        ["Kasia", "Kowalska", 4],
        ["Jan", "Nowak", 4]]

with open("oceny.csv", "w") as csvfile:
    writer = csv.writer(csvfile) # tworzymy "pisarza" csv
    for wpis in data:
        writer.writerow(wpis)    
%%bash
cat oceny.csv
imie,nazwisko,ocena
Kasia,Kowalska,4
Jan,Nowak,4

Wczytywanie danych


import csv

with open('oceny.csv', 'r') as csvfile:
    reader = csv.reader(csvfile) # tworzymy "czytelnika" csv
    for wpis in reader:
        print(wpis)
['imie', 'nazwisko', 'ocena']
['Kasia', 'Kowalska', '4']
['Jan', 'Nowak', '4']

Przykład - ankieta


%%bash
cat ankieta.csv | sed -n 1p # pierwsza linia
Sygnatura czasowa,"Co sądzisz o ""przemycaniu"" na wykładzie podstawowych pojęć z innych dziedzin informatyki?",Tempo wykładu jest,Czy na wykładzie powinno być więcej przykładów?,"Ostatnie wykłady (jeśli starczy czasu, po przerobieniu całego podstawowego materiału) powinny być poświęcone"
%%bash
cat ankieta.csv | sed -n 2p # druga linia
2016-11-04 09:38:13,"Jak najbardziej, o ile nie będzie tego na egzaminie.",Zbyt szybkie! Nie nadążam z przyswajaniem nowych pojęć.,"Raczej tak, przykłady trochę ułatwiają przyswojenie nowych pojęć.",Powtórce. Przegląd wszystkich omawianych wcześniej zagadnień.

Ankieta - wczytywanie danych


import csv

# wczytaj wyniki ankiety do zmiennej wyniki
with open('ankieta.csv', 'r') as csvfile:
    reader = csv.reader(csvfile)
    wyniki = list(reader) # zapisz wyniki
headers = wyniki.pop(0) # pierwszy wiersz to pytania

print(headers)
['Sygnatura czasowa', 'Co sądzisz o "przemycaniu" na wykładzie podstawowych pojęć z innych dziedzin informatyki?', 'Tempo wykładu jest', 'Czy na wykładzie powinno być więcej przykładów?', 'Ostatnie wykłady (jeśli starczy czasu, po przerobieniu całego podstawowego materiału) powinny być poświęcone']
print(wyniki[0]) # odpowiedzi pierwszej osoby
['2016-11-04 09:38:13', 'Jak najbardziej, o ile nie będzie tego na egzaminie.', 'Zbyt szybkie! Nie nadążam z przyswajaniem nowych pojęć.', 'Raczej tak, przykłady trochę ułatwiają przyswojenie nowych pojęć.', 'Powtórce. Przegląd wszystkich omawianych wcześniej zagadnień.']

Ilość odpowiedzi


n_votes = len(wyniki)

print("W ankiecie wzięło {} osób.".format(n_votes))
W ankiecie wzięło 22 osób.

Sygnatura czasowa


from datetime import datetime

def str2date(date):
    """Konwertuje string do datetime wg formatu z ankiety."""
    return datetime.strptime(date, '%Y-%m-%d %H:%M:%S')

sygnatury = []

for glos in wyniki:
    sygnatury.append(str2date(glos[0]))
print("Między pierwszym a ostatnim głosem minęło:",
      sygnatury[-1] - sygnatury[0])
Między pierwszym a ostatnim głosem minęło: 11 days, 15:37:12

Liczba głosów w czasie


%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter

dates = matplotlib.dates.date2num(sygnatury)

fig, ax = plt.subplots()
ax.plot_date(dates, range(n_votes))

ax.xaxis.set_major_formatter(DateFormatter('%m-%d'))

png

Zliczanie głosów


def count_votes(odpowiedzi):
    """Liczy ilość głosów per odpowiedź."""
    results = {} # odpowiedź: liczba głosów

    while len(odpowiedzi): # dopóki pozostały odpowiedzi
        odpowiedz = odpowiedzi[0] # weź pierwszą z brzegu
        n_votes = odpowiedzi.count(odpowiedz) # liczba wystąpień
        results[odpowiedz] = n_votes # zapisz
        # usuń wszystkie wystąpienia odpowiedz
        odpowiedzi = [o for o in odpowiedzi if o != odpowiedz]

    return results

Analiza pytania


def analizuj(n, m=3):
    """Drukuje m najczęstszych odpowiedzi na n-te pytanie."""
    pytanie = headers[n] # treść pytania
    odpowiedzi = [o[n] for o in wyniki] # n-ta kolumna

    votes = count_votes(odpowiedzi) # {odp: liczba głosów}
    # posortuj głosy -> odpowiedzi od najczęstszych
    best = sorted(votes, key=votes.get, reverse=True)

    # wyrdukuj pytanie
    print("-"*len(pytanie))
    print(pytanie)
    print("-"*len(pytanie), end="\n\n")

    # wydrukuj m najczęstszych odpowiedzi
    for i in range(m):
        odp = best[i] # i-ta odpowiedz
        procent = votes[odp] / n_votes * 100 # procent głosów
        print("{:.2f}% -> {}".format(procent, odp))

Pytanie 1


analizuj(1)
-----------------------------------------------------------------------------------------
Co sądzisz o "przemycaniu" na wykładzie podstawowych pojęć z innych dziedzin informatyki?
-----------------------------------------------------------------------------------------

40.91% -> Jak najbardziej! Wszystko związane z programowaniem jest mile widziane.
36.36% -> Jak najbardziej, o ile nie będzie tego na egzaminie.
13.64% -> Chętnie poznam, ale nie za dużo.

Pytanie 2


analizuj(2)
------------------
Tempo wykładu jest
------------------

40.91% -> Trochę za szybkie. Można minimalnie zmniejszyć liczbę nowych pojęć per wykład.
22.73% -> Zbyt szybkie! Nie nadążam z przyswajaniem nowych pojęć.
22.73% -> Idealne.

Pytanie 3


analizuj(3)
-----------------------------------------------
Czy na wykładzie powinno być więcej przykładów?
-----------------------------------------------

45.45% -> Raczej tak, przykłady trochę ułatwiają przyswojenie nowych pojęć.
36.36% -> Jest dobrze jak jest.
9.09% -> Zdecydowanie tak! Nic tak nie uczy jak dobry przykład.

Pytanie 4


analizuj(4, 4)
------------------------------------------------------------------------------------------------------------
Ostatnie wykłady (jeśli starczy czasu, po przerobieniu całego podstawowego materiału) powinny być poświęcone
------------------------------------------------------------------------------------------------------------

40.91% -> Powtórce. Przegląd wszystkich omawianych wcześniej zagadnień.
22.73% -> GUI. Graficzny interfejs użytkownika.
18.18% -> Tworzenie gier.
13.64% -> Bazy danych.