Podstawy Programowania w Pythonie

Czas trwania: 13:00-17:00 (XI PIWO) + gdzieś w środku pizza
Prowadzący: Konrad Siek <konrad.siek@cs.put.edu.pl>
Strona: www.cs.put.poznan.pl/ksiek/talks/python.html

Czemu programować?

  • ... ?

  • Bo można rozwiązywać własne problemy
  • Bo można grzebać w open-source'owych aplikacjach
  • Bo można złapać dobrą pracę w przyszłości
  • Bo mozna być jeszcze większym nerdem niż się jest
    Bo programowanie jest ciekawym i rozwijajacym zajęciem

Czemu Python?

  • Bo jest prosty i elegancki
  • Bo jest popularny
  • Bo jest dobrze wpierany
  • Bo jest do niego dużo bibliotek

Narzędzia

  • Linuksowy terminal i jakiś edytor tekstowy (np. gedit)
  • Interpreter: python lub, lepiej, ipython
  • Zintegrowane środowisko programistyczne: PyCharm <https://www.jetbrains.com/pycharm/>

Python 2 (2.7) vs Python 3 (3.2)

Ahoj, przygodo?

print("Hello world!")

Ahoj, plikowa przygodo!

Plik z programem:

  1. Stwórz gedit hello_world.py
    print("Hello world!")
  2. Uruchom przez interpreter: python hello_world.py lub python3 hello_world.py

Ahoj, samowykonywalna przygodo!

Program jako plik wykonywalny:

  1. Stwórz (edytuj) plik: gedit hello_world.py
    #!/usr/bin/env python3
    
    print("Hello world!")
  2. Nadaj prawa do wykonywania: chmod a+x hello_world.py
  3. Uruchom jako skrypt: ./hello_world.py

Kilka operatorów

Programowanie jak matematyka:

2 + 2
2 - 2
2 * 2
2 / 2
2 % 2
2 ** 2

Kilka operatorów

Programowanie jak matematyka:

2 + 2   # dodawanie
2 - 2   # odejmowanie
2 * 2   # mnożenie
2 / 2   # dzielenie
2 % 2   # reszta z dzielenia - dzielenie modulo
2 ** 2  # potęgowanie

Kilka operatorów

Operatory dla napisów (string-ów)

"a" + "b"
"x" * 10
"a %s c" % ("b")

Kilka operatorów

Operatory dla napisów (string-ów)

"a" + "b"         # konkatenacja
"x" * 10          # zwielokrotnianie
"a %s c" % ("b")  # podstawianie - α-konwersja

Konsola vs plik

  1. Utwórz plik z przykładowymi użyciami operatorów: gedit ops.py
    #!/usr/bin/env python3
    
    2 ** 10
    "x" * 100
  2. Nadaj prawa do uruchomienia i uruchom
  3. ... ?

Konsola vs plik

A teraz?

#!/usr/bin/env python3

print(2 ** 10)
print("x" * 100)

Zmienne

Przypisywanie wartości zmiennej:

x = 5
y = 2

Stosowanie zmiennej w wyrażeniach:

x + 1
3 * x ** 2 + y
"x" * x

Przypisywanie zmiennej do innej zmiennej:

x = y

Modyfikacja zmiennej

Inkrementacja (dodawanie 1 do zmiennej):

x = x + 1

Specjalny operator, który robi to samo:

x += 1

Podobne operatory:

x -= 1      # to samo co: x = x - 1
x *= 2      # to samo co: x = x * 2
x /= 3      # to samo co: x = x / 3
x %= 4      # to samo co: x = x % 4
s += "a"    # to samo co: s = s + "a"

Zadanie

Mamy dane dwie zmienne: x i y o dowolnych wartościach. Np.

x = "sól"
y = "pieprz"

Napisz program który podmieni wartości tych zmiennych między sobą.

T.j. na wyjściu:

  • x ma taką wartość jaką y miało na wejściu
  • y ma taką wartość jaką x miało na wejściu

Wypisz na ekran zawartość x i y po podmienieniu.

Rozwiązanie

x = "sól"
y = "pieprz"

tmp = x
x = y
y = tmp

print("x =", x, "y =", y)

Zmienne różnych typów

Zmienne całkowitoliczbowe (integer):

i = 5
type(i)     # -> int

Zmienne napisowe (string):

s = "hello world"
type(s)     # -> str

Zmienne różnych nowych typów

Zmienne zmiennoprzecinkowe (floating-point):

f = 1.5
f = 1.
f = .5
type(f)     # -> float

Zmienne prawda/fałsz (algebra Boole'a, Boolean):

b = True
b = False
type(b)     # -> bool

Konwersja między typami

str(5)      # -> "5"
float(5)    # -> 5.0
int(5.2)    # -> 5
bool(5)     # -> True

Dla jakiej wartości int-a, float-a, string-a typ bool przyjmuje wartość False?

Konstrukcje warunkowe

Wyrażenie jeżeli (if statement)

if x == 5:
    print(x, "is 5")
Ważne elementy:
  • słowo kluczowe if
  • porównanie z operatorem ==
  • dwukropek
  • wcięcie (1 tab = 4 spacje) oznacza blok kodu

Czemu operator ma dwa znaki a nie jeden?

Konstrukcje warunkowe

A co jeśli nie? (else statement)

if x == 5:
    print(x, "is 5")
else:
    print(x, "is not 5")

Konstrukcje warunkowe

Dodatkowe warunki (else-if):

if x == 5:
    print(x, "is 5")
elif x == 7:
    print(x, "is 7")
else:
    print(x, "is neither 5 nor 7")

Konstrukcje warunkowe

Więcej warunków...

if x == 5:
    print(x, "is 5")
elif x == 7:
    print(x, "is 7")
elif x == 9:
    print(x, "is 9")
else:
    print(x, "is neither 5, 7 nor 9")

Konstrukcje warunkowe

Zagnieżdżanie warunków:

if x == 5:
    print(x, "is 5")
    if y == 5:
        print (y, "is y")
elif x == 7:
    print(x, "is 7")
    if y == 5:
        print (y, "is 7")
elif x == 9:
    print(x, "is 9")
        if y == 9:
            print (y, "is 9")
else:
    print(x, "is neither 5, 7 nor 9")
    print(y, "is some number")
Co zrobi ten program?
  • x=9 y=9
  • x=7 y=9
  • x=4 y=5

Konstrukcje warunkowe

Operatory logiczne:

x == y      # x równe y
x < y       # x mniejsze niż y
x > y       # x większe niż y
x <= y      # x mniejsze lub równe y
x >= y      # x większe lub równe y
x != y      # x nierówne y

Czy operatory działają na napisach i bool-ach?

Konstrukcje warunkowe

Złożone wyrażenia:

if x > 0 and y > 0:
    print("a")

if x <= 0 or y <= 0:
    print("b")

Zaprzeczenie:

if not z % 10:
    print("c")

if not z % 10 and not z % 3:
    print("d")

Zadanie

Mamy daną zmienną: x o typie int i dowolnej wartości, np.

Napisz program który:
  • wypisuje na ekran fizz jeśli x jest podzielne przez 3
  • wypisuje na ekran buzz jeśli x jest podzielne przez 5
  • (więc wypisuje na ekran fizzbuzz jeśli x jest podzielne przez 3 i 5)
  • wypisuje na ekran x jeśli x nie jest podzielne przez 3 ani 5

(Czyli klasyczny fizzbuzz.)

Rozwiązanie

output = ""

if x % 3 == 0:
    output += "fizz"

if x % 5 == 0:
    output += "buzz"

if not x % 3 == 0 and not x % 5 == 0:
    output += str(x)

print output

Pętle!

Powtarzanie bloku kodu:

x = 0
while x < 100:
    print(x)
    x += 1

Powtarzaj instrukcje:

print(x)
x += 1

... tak długo, jak x < 100 jest prawdziwe.

Pętle!

Powtarzanie bloku kodu:

x = 0
while x < 100:
    print(x)
    x += 1
Ważne elementy:
  • słowo kluczowe while
  • warunek (z operatorem <)
  • dwukropek
  • ciało pętli (instrukcje w środku)
  • przed pętlą: inicjalizacja licznika

Pętle!

Zagnieżdżanie pętli:

x = 0
y = 0
while x < 10:
    while y < 10:
        print(".")
        y += 1
    x += 1

Ile kropek będzie wyświetlonych?

Zadanie

Mamy daną zmienną: n typu int o wartości 1-20

Napisz program który rysuje trójkąt (prostokątny, równoramienny) o wysokości n z gwiazdek.

Np. dla n = 6:

*
**
***
****
*****
******

Hint:

help(print)
print("x", end="")

Rozwiązanie

Dwię pętle:

n = 6

i = 1
while i <= n:
    j = 0
    while j < i:
        print("*", end="")
        j += 1
    print()
    i += 1

Rozwiązanie

Mnożenie napisów:

n = 6

i = 0
while i < n:
    print("*" * (i + 1))
    i += 1

Listy!

Specjalny typ do przechowywania wielu wartości:

li = [1, 2, 3, 4]
ls = ["a", "b", "c"]
lm = [1, "a", 2, "c",]
ll = [[1, 2], [3, 4], [5,6]]
le = []

type(li)
type(ls)
type(lm)
type(le)

Lista pamięta kolejność elementów. Elementy mogą być dowolnego typu.

Listy!

Wyciąganie elementów listy:

l = ["asia", "basia", "casia", "dasia"]

l[0]    # -> "asia"
l[1]    # -> "basia"
l[2]    # -> "casia"
l[3]    # -> "dasia"

len(l)  # -> 4

Indeksowanie od 0 - typowe dla programowania.

Jak wyciągnąć ostatni element? (klasycznie i pythonowo)

Zadanie

Dana jest lista l zawierająca integery. Np. :

l = [4, 6, 7, 13, 21, 42, 144]

Napisz program który wypisze każdy z elementów listy l w nowej lini. Np.:

4
6
7
13
21
42
144

Rozwiązanie

Iteracja po wszystkich elementach listy za pomocą pętli while:

l = [4, 6, 7, 13, 21, 42, 144]
i = 0

while i < len(l):
    print(l[i])
    i += 1

Lepsza pętla

Pętla for iteruje po strukturach danych takich jak listy:

l = [4, 6, 7, 13, 21, 42, 144]

for element in l:
    print(element)

Znowu zadanie!

Dana jest lista l zawierająca listy, które zawierają integery. Np. :

l = [[2], [4, 8], [16, 32, 64]]

Napisz program który wypisze każdą liczbę w nowej lini. Np.:

2
4
8
16
32
62

Rozwiązanie

l = [[2], [4, 8], [16, 32, 64]]

for sublist in l:
    for element in sublist:
        print(element)

Więcej o listach

Wyciąganie podlisty więcej niż jednego elementu z listy:

l = [1, 2, 3, 4, 5, 6]

l[0:3]      # -> [1, 2, 3]
l[3:6]      # -> [4, 5, 6]
l[7:9]      # -> []

l[:3]       # -> [1, 2, 3]
l[3:]       # -> [4, 5, 6]
l[:]        # -> [1, 2, 3, 4, 5, 6]

l[-2:]      # -> [5, 6]

Podlista jest zupełnie nową listą, kopią.

Tęskniliście?

Dana jest lista l zawierająca integery. Np. :

l = [2, 4, 8, 16, 32]

Napisz program który wypisze wszystkie prefiksy tej listy. Prefix to taka podlista jakiejś innej listy, która zaczyna się tak samo jak ta inna lista.

[]
[2]
[2, 4]
[2, 4, 8]
[2, 4, 8, 16]
[2, 4, 8, 16, 32]

Rozwiązanie

l = [2, 4, 8, 16, 32]

i = 0
while i < len(l):
    print(l[:i + 1])
    i += 1

Modyfikowanie list

Dodawanie do listy:

l = [1, 2, 3]

l = l + [5, 6]

Podmiana elementu:

l[3] = -1

Usuwanie z listy:

del l[0]

Once more unto the breach, dear friends, once more!

Dana jest lista l zawierająca integery. Np. :

l = [1, 2, 3, 4, 5]

Napisz program który stworzy nową listę r, która wypisze będzie zawierać kwadraty wszysztkich liczb z l. Na końcu wypisz listę r.

Oczekiwane wyjście dla wejścia powyżej:

[1, 4, 9, 16, 25]

Rozwiązanie

l = [1, 2, 3, 4, 5]
r = []

for e in l:
    r += [e ** 2]

print(r)

Co można zrobić z listą?

Listy dostarczają różnych metod do wykonywania działań na ich zawartości:

  • Sortowanie listy:
    l.sort()
  • Odwracanie listy:
    l.reverse()
  • Liczenie elementów w liście:
    l.count(4)
  • Dokładanie na/sciąganie z końca listy:
    l.append(4)
    l.pop()         # -> 4

Pseudozadanie

Napiszemy pętle która dla dowolnej listy l zawierającej liczby, stworzy analogiczną listę zawierającą wartości bezwzględne. Np. :

l = [1, -1, 2, -2, 3, -3]

r = [1, 1, 2, 2, 3, 3]
l = [1, -1, 2, -2, 3, -3]

r = []
for e in l:
    if e < 0:
        r += [-e]
    else:
        r += [e]

Pseudozadanie

A teraz ... napiszemy jeszcze jedną pętle która dla dowolnej listy l2 zawierającej liczby, stworzy analogiczną listę zawierającą wartości bezwzględne. Np. :

l2 = [13, -12, 21, -20, 31, -32]

r2 = [13, 12, 21, 20, 31, 32]
l = [1, -1, 2, -2, 3, -3]
l2 = [1, -1, 2, -2, 3, -3]

r = []
for e in l:
    if e < 0:
        r += [-e]
    else:
        r += [e]

r2 = []
for e in l2:
    if e < 0:
        r += [-e]
    else:
        r += [e]

Funkcje i procedury

Zamykamy blok kodu wewnątrz funkcji:

def abs_list(l):
    r = []
    for e in l:
        if e < 0:
            r += [-e]
        else:
            r += [e]
    return r

Ważne elementy:

  • słowo kluczowe def do definiowania funkcji
  • nazwa funkcji abs_list
  • lista parametrów (w nawiasach) - tutaj tylko jeden paramet l, ale może być mniej, lub więcej, oddzielonych przecinkami
  • dwukropek, wcięcie, kod, ...
  • słowo kluczowe return które zwraca wartość (chociaż ogólnie funkcja nie musi zwracać wartości)

Funkcje i procedury

Wywoływanie funkcji (aka aplikacja funkcji):

def abs_list(l):
    r = []
    for e in l:
        if e < 0:
            r += [-e]
        else:
            r += [e]
    return r

l = [1, -1, 2, -2, 3, -3]
l2 = [1, -1, 2, -2, 3, -3]

r = abs_list(l)
r2 = abs_list(l2)

Zasięg zmiennych

Czy zmienna zdefiniowana wewnątrz funkcji jest widoczna na zewnątrz funkcji?

def f():
    inner = 44

f()
print(inner)

Zasięg zmiennych

Czy zmienna zdefiniowana na zewnątrz funkcji jest widoczna na zewnątrz funkcji?

outer = 6

def g():
    print(outer)

def h():
    outer = 7

g()
h()

Zasięg zmiennych

Wykonywanie metod:

li = [1, 2, 3]

def foo():
    li.append(4)

foo()
print(li)

Zwracanie wartości przez argumenty

Wykonywanie metod:

x = 0
l = [1, 2, 3]

def foo(x, l):
    x = 1
    l.append(4)

foo(x, l)
print(x, l)

Wywołanie funkcji z wieloma argumentami

def square_poly(x, a, b, c):
    return a * x ** 2 + b * x + c

for e in [1, 2, 3, 4, 5, 6]:
    y = square_poly(e, 2, -4, 2)
    print(y)

for e in [1, 2, 3, 4, 5, 6]:
    y = square_poly(a=2, b=-4, c=2, x=e)
    print(y)

Wartości domyślne

def square_poly(x, a=0, b=0, c=0):
    return a * x ** 2 + b * x + c

for e in [1, 2, 3, 4, 5, 6]:
    y = square_poly(e, b=2)
    print(y)

Procedura vs funkcja

Funkcja - jak w matematyce:

  • deterministyczna
  • zwraca wynik przez return
  • nie ma efektów ubocznych: nie modyfikuje zmiennych na zewnątrz funkcji, nie wyświetla nic na ekran itp.

Procedura:

  • nie + w/w

"Zaawansowane" cechy funkcji

Tyle do powiedzenia, tak mało czasu!

def fun(n):
    def more_fun(e="", i=0):
        if (i < n):
            return [e] + more_fun(i+1)
        else:
            return []
    return more_fun

magic = fun

magic(5)(".")

Zadanie

Napisz funkcję która przyjmuje jeden argument-listę. Każdy element listy to średnia ocen jakiegoś studenta <2.0, 5.0>. Napisz (i przetestuj) funkcję która wystawia ocenę końcową - zwraca listę gdzie dla każdej oceny na liście wejściowej, na liście wyjściowej:

  • dla przedziału <2.0, 3.0) jest 2.0
  • dla przedziału <3.0, 3.5) jest 3.0
  • dla przedziału <3.5, 4.0) jest 3.5
  • dla przedziału <4.0, 4.5) jest 4.0
  • dla przedziału <4.5, 5.0) jest 4.5
  • dla wartości 5.0 jest 5.0

Good place to start:

def grade(averages):
    pass

averages = [2.5, 3.1, 3.6, 4.0, 4.9, 5.0]
grades = grade(averages)
print(grades)

Rozwiązanie

def grade(averages):
    r = []
    for e in averages:
        if e < 3.:
            r += [2.]
        elif e < 3.5:
            r += [3.]
        elif e < 4.:
            r += [3.5]
        elif e < 4.5:
            r += [4.]
        elif e < 5:
            r += [4.5]
        else:
            r += [5]
    return r

averages = [2.5, 3.1, 3.6, 4.0, 4.9, 5.0]
grades = grade(averages)
print(grades)

Słowniki

Jeszcze jedna przydatna struktura: słownik asocjacyjny:

d = {1: "a", 2: "b", 3: "b"}

Ważne elementy:

  • Nawiasy klamrowe
  • Zbiór kluczy (1, 2, 3) - klucze są unikalne, kolejność jest zapominana
  • Wartości ("a", "b", "c")

Używanie:

d[1]            # -> "a"
d[4] = "d"
del d[1]

type(d)         # -> dict

1 in d          # -> False
2 in d          # -> True

Słowniki

Przeglądanie zawartości:

d = {"a": 1, "b": 2, "c": 3, "d": 4}

d.keys()        # -> [2, 3, 4]
d.values()      # -> ["b", "c", "d"]

for key in d:
   print(d[key])

Co można zrobić ze słownikami?

Zastosowanie à la funkcja:

grade_labels = {
    2.: "ndst",
    3.: "dop",
    3.5: "dop+",
    4.0: "db",
    4.5: "db+",
    5.0: "bdb"
}

averages = [2.5, 3.1, 3.6, 4.0, 4.9, 5.0]
grades = grade(averages)

r = []
for e in grades
    r += [grade_labels[e]]

Co można zrobić ze słownikami?

Zastosowanie à la struktura danych:

grade_labels = {
    "inf71746": [3., 4., 5.],
    "inf71748": [4., 3., 5.],
    "inf71749": [2., 3., 4.],
    "inf71750": [3., 2., 3.],
    "inf71751": [5., 3., 4.],
}

Zadanie

Napisz funkcję która przyjmuje napis jako argument i zwraca słownik, który zawiera histogram tego napisu. Histogram mówi nam ile razy każda litera wystąpiła w tym napisie.

Np. dla napisu "ala ma kota" histogram to

histogram = {
    "a": 4,
    "l": 1,
    "m": 1,
    "k": 1,
    "o": 1,
    "t": 1,
    " ": 2,
}

Informacja pomocnicza - napisy można traktować jak listę napisów zawierających pojedyncze znaki.

for letter in "ala ma kota"
    print(letter)

Rozwiązanie

def histogram(text):
    histogram = {}

    for letter in text:
        if letter in histogram:
            histogram[letter] += 1
        else:
            histogram[letter] = 1

    return histogram

Pliki

Stwórz plik plik.txt o treści:

11, 12, 13, 14
21, 22, 23, 24
31, 32, 33, 34
41, 42, 43, 44

Pliki

Wczytaj plik (w trybie tylko-do-odczytu):

f = open("plik.txt", "r")

f.read()        # -> cała zawartość pliku jako string
f.readlines()   # -> cała zawartośc pliku jako lista stringów

f.close()

Czytanie po kawałku (rzadko używane w praktyce):

f = open("plik.txt", "r")

f.readline()    # -> czyta jedną linię, przenosuwa wskaźnik
f.seek(n)       # przesuwa wskaźnik o n bajtów
f.tell(n)       # obecna pozycja wskaźnika
f.read(n)       # czytaj n bajtów

f.close()

Pliki

Zapis do pliku:

f = open("plik.txt", "w")

f.write(s)        # zapisuje string do pliku
f.writelines(l)   # zapisuje listę stringów do pliku

f.close()

Pliki

Linie mają znak nowej lini "n" na końcu - nie jest potrzebny:

f = open("plik.txt", "r")

r = []
for line in f.readlines():
    line = line[:-1]

    r += [line]

f.close()

println(r)

Pliki

Podział pliku wg przecinków:

f = open("plik.txt", "r")

r = []
for line in f.readlines():
    line = line[:-1]
    fields = line.split(",")

    r += [fields]

f.close()

println(r)

Pliki

Usuwanie zbędnych spacji:

f = open("plik.txt", "r")

r = []
for line in f.readlines():
    line = line[:-1]
    fields = line.split(",")

    clean_fields = []
    for field in fields:
        clean_field = field.strip()
        clean_fields += [clean_field]

    r += [fields]

f.close()

println(r)

Stdlib

Python posiada rozległą bibliotekę standardową która rozwiązuje wiele problemów. Jak się dowiedzieć co w niej jest?

Zadanie

Ściągnij plik tekstowy http://www.gutenberg.org/cache/epub/150/pg150.txt na dysk. Następnie napisz program usuwa z tekstu wszystkie znaki oprócz liter (i spacji) i zamienia małe litery na wielkie. Następnie użyj oczyszczonego tekstu żeby stworzyć histogram słów dla tego pliku. Następnie zapisz histogram jako plik histogram.txt na dysku w następującej formie:

EXPLAIN: 13
PARAGRAPHS: 3
ENVIRONMENT: 1
DEMAND: 7
OVERWHELMING: 1

Uwaga: do stworzenia programu brakuje Ci informacji!

Rozwiązanie

def get_content(path):
     f = open(path, "r")
     content = f.read()
     f.close()
     return content

 def clean_content(content):
     clean_content = ""
     for letter in content:
         if letter.isalpha() or letter.isspace():
             clean_content += letter.upper()
     return clean_content

 def histogram(words):
     histogram = {}
     for word in words:
         if word in histogram:
             histogram[word] += 1
         else:
             histogram[word] = 1
     return histogram

 def write_to_file(path, hist):
     f = open(path, "w")
     for key in hist:
         f.write(key + ": " + str(hist[key]) + "\n")
     f.close()

 text = get_content("pg150.txt")
 text = clean_content(text)
 words = text.split()
 hist = histogram(words)
 write_to_file('histogram.txt', hist)

Obiekty

Paradygmat obiektowy (object-oriented programming): wszystko jest obiektem.

Obiekt:

  • atrybuty (pola) - stan
  • procedury (metody) - kod który operuje na stanie
  • klasa - obiekt należy do szerszej klasy/typu obiektów (podejście klasowe)

Wszechobecne podejście.

Cały czas używamy obiektów.

Obiekty

Definicja klasy:

class Kitty (object):

    mood = "neutral"    # pole
    food = "hungry"     # pole

kitty1 = Kitty()    # instancjalizacja -> instancja/obiekt
kitty2 = Kitty()    # instancjalizacja -> instancja/obiekt

print(type(kitty1), kitty1.mood, kitty1.food)
print(type(kitty2), kitty2.mood, kitty2.food)

print(kitty1, kitty2)

Ważne elementy:

  • słowo kluczowe class
  • oznaczenie że jest typem obiektowym (object)
  • nazwa klasy Kitty
  • definicje pól
  • tworzenie obiektów danej klasy
  • dostęp do pól obiektu przez kropkę

Obiekty

Metody:

class Kitty (object):

    mood = "neutral"
    food = "hungry"

    def feed(self):
        self.food = "fed"

    def pet(self):
        if self.food == "fed":
            self.mood = "happy"

kitty = Kitty()
kitty.pet()
kitty.feed()

Ważne elementy:

  • metoda zdefiniowana jak funkcja
  • pierwszy argument metody to referencja do obiektu (w 99.999% przypadków nazwany self)
  • wywołanie metody przez kropkę, jeden mniej argument (bo self jest uzupełniany automagicznie)

Obiekty

Konstruktor:

class Kitty (object):

    mood = "neutral"
    food = "hungry"

    def __init__(self, name):
        self.name = name

    def feed(self):
        self.food = "fed"

    def pet(self):
        if self.food == "fed":
            self.mood = "happy"

kitty = Kitty("Mr Bojangles")
print(kitty.name)

Ważne elementy:

  • metoda o specjalnej nazwie (dwa podkreślniki z przodu i z tyłu)
  • przypisanie dynamicznie tworzy nowe pole

Obiekty

Inne magiczne metody:

class Kitty (object):

    mood = "neutral"
    food = "hungry"

    def __init__(self, name):
        self.name = name

    def __cmp__ (self, other_kitty):
        if self.name == other_kitty.name:
            return 0                        # objects are equal
        elif self.name < other_kitty.name:
            return -1                       # this object is smaller
        else:
            return 1                        # this object is larger

    def __repr__(self):
        return self.name

kitty1 = Kitty("Mr Bojangles")
kitty2 = Kitty("Mr Fluffball")

print(kitty1, kitty2)
print(kitty1 < kitty2)

Więcej: http://www.rafekettler.com/magicmethods.html (np. kitty1 + kitty2, kitty1[0]).

Obiekty

Hierarchie klas: tworzymy klasy Dog i Cat które mają podobne charakterystyki, które wywodzą się ze wspólnej nadklasy: HousePet:

                        +------------+
                        | HousePet   |
                        +------------+
                        | pet()      |
                        | feed()     |
                        +------------+
                           ^      ^
                           |      |
              +------------+      +-----------+
              |                               |
+---------------------------+       +------------------+
| Cat                       |       | Dog              |
+---------------------------+       +------------------+
| walk_on_top_of(furniture) |       | bark_all_night() |
+---------------------------+       +------------------+

Dziedziczenie klas

class HousePet (object):
    def pet(self):
        print("pet")

    def feed(self):
        print("feed")

class Cat (HousePet):
    def __init__(self, name):
        self.name = name

    def walk_on_top_of(self, furniture):
        print("walk on top of " + str(furniture))

class Dog (HousePet):
    def __init__(self, name):
        self.name = name

    def bark_all_night(self):
        print("bark all night")

cat = Cat("Mr Bojangles")
dog = Dog("Holly")

cat.pet()
dog.pet()

Ważne elementy:

  • zamiast dziedziczyć po obiekcie, dziedziczenie po innej klasie

Przeciążanie metod

class Cat (HousePet):
    def __init__(self, name):
        self.name = name

    def pet(self):
        print("run away")

    def feed(self):
        super(Cat, self).feed()
        print("meow endlessly")

    def walk_on_top_of(self, furniture):
        print("walk on top of " + str(furniture))

cat = Cat("Mr Bojangles")
cat.feed()
cat.pet()

Ważne elementy:

  • proste redefiniowanie metod
  • super zwraca wersje obiektu w typie nadklasy

Zadanie

Napisz klasę Employees, która jest książką kontaktów do pracowników w firmie Apples & Oranges pozwalająca dodawać, usuwać kontakty, wypisywać kontakty.

Firma dla każdego pracownika trzyma następujące informacje: imię, nazwisko, telefon, email, nr legitymacji.

Firma Apples & Oranges zatrudnia dwa profile pracowników: programistów i hodowców roślin.

Dla programistów firma trzyma także informację o tym w jakim języku programowania dany programista pisze.

Dla hodowców roślin, firma trzyma informację, jaką rośliną się zajmują.

Te informacje są ważne dla szefów w Apples & Oranges, więc Employees pozwala także wypisywać wszystkich programistów którzy piszą w konkretnym języku i wszystkich hodowców którzy hodują konkretną roślinę.

Może czas w końcu włączyć PyCharma?

Rozwiązanie

class Employees (object):
    contacts = []

    def add(self, employee):
        pass

    def remove(self, employee_id):
        pass

    def print_all(self):
        pass

    def print_language(self, language):
        pass

    def print_plant(self, plant):
        pass

class Employee (object):
    pass

class Programmer (Employee):
    pass

class Planter (Employees):
    pass

Rozwiązanie

class Employees (object):
   contacts = []

   def add(self, employee):
       self.contacts += [employee]

   def remove(self, employee_id):
       for contact in self.contacts:
           if contact.id == employee_id:
               self.contacts.remove(contact)

   def print_all(self):
       for contact in self.contacts:
           print(contact)

   def print_by_language(self, language):
       for contact in self.contacts:
           if (type(contact) == Programmer):
               if (contact.language == language):
                   print(contact)

   def print_by_plant(self, plant):
       for contact in self.contacts:
           if (type(contact) == Planter):
               if (contact.plant == plant):
                   print(contact)

Rozwiązanie

class Employee (object):
    def __init__(self, id, name, surname, telephone, email):
        self.id = id
        self.name = name
        self.surname = surname
        self.telephone = telephone
        self.email = email

    def __repr__(self):
        return str(self.id) + " " + self.name + " " + self.surname + " " + self.telephone
                + " " + self.email

class Programmer (Employee):
    def __init__(self, id, name, surname, telephone, email, language):
        super(Programmer, self).__init__(id, name, surname, telephone, email)
        self.language = language

    def __repr__(self):
        return str(self.id) + " " + self.name + " " + self.surname + " " + self.telephone
                + " " + self.email + " " + self.language


class Planter (Employee):
    def __init__(self, id, name, surname, telephone, email, plant):
        super(Planter, self).__init__(id, name, surname, telephone, email)
        self.plant = plant

    def __repr__(self):
        return str(self.id) + " " + self.name + " " + self.surname + " " + self.telephone
                + " " + self.email + " " + self.plant

Rozwiązanie

employees = Employees()

employees.add(Programmer(1, "Jan", "Kowalski", "665-0001", "jkowalski@aeto.com", "python"))
employees.add(Programmer(2, "Jan", "Nowak", "665-0002", "jnowak@aeto.com", "python"))
employees.add(Programmer(3, "Jan", "Wojciechowski", "665-0003", "jw@aeto.com", "java"))

employees.add(Planter(4, "Tomasz", "Kowalski", "665-0004", "tkowalski@aeto.com", "orange"))
employees.add(Planter(5, "Tomasz", "Nowak", "665-0005", "tnowak@aeto.com", "orange"))
employees.add(Planter(6, "Tomasz", "Wojciechowski", "665-0006", "tw@aeto.com", "banana"))

print("All employees")
employees.print_all()

print("All Python programmers")
employees.print_by_language("python")

print("All banana planters")
employees.print_by_plant("banana")

Okienka

Zaimportujmy moduł

import tkinter as tk

Słowo kluczowe import - ładuje zawartość pliku Tkinter.py (z bieżącego katalogu lub gdzieś z systemu).

Słowo kluczowe as - pozwala zmienić długą nazwę modułu na coś poręcznego

(Jeśli nie działa to: sudo apt-get install python3-tk ...)

Okienka

Pierwsze okienko!

import tkinter as tk

window = tk.Tk()

label = tk.Label(window, text="Hello")
label.pack()

window.mainloop()

Ważne elementy:

  • Tworzymy obiekt okna
  • Tworzymy w oknie obiekt etykiety z napisem
  • Pakujemy etykietę, żeby pasowała rozmiarem do okna
  • Włączamy system okienkowy

Przycisk

import tkinter as tk

window = tk.Tk()

label = tk.Label(window, text="Hello")
label.pack()

def poke():
    print("Ow!")

button = tk.Button(window, text="Poke", command=poke)
button.pack()

window.mainloop()

Ważne elementy:

  • Dodajemy przycisk
  • Przycisk wykonuje po naciśnięciu funkcję

Przycisk

Bardziej interaktywny przycisk:

import tkinter as tk

window = tk.Tk()

label_text = tk.StringVar()
label_text.set("Hello")

label = tk.Label(window, textvariable=label_text)
label.pack()

def poke():
    label_text.set("Ow!")

button = tk.Button(window, text="Poke", command=poke)
button.pack()

window.mainloop()

Ważne elementy:

  • Dodajemy zmieniający się tekst do etykiety
  • Przycisk po naciśnięciu zmienia napis na etykiecie

Ramka

Układanie widgetów:

import tkinter as tk

window = tk.Tk()

frame = tk.Frame(window)
frame.pack(fill="x")

label_text = tk.StringVar()
label_text.set("Hello")

label = tk.Label(frame, textvariable=label_text)
label.pack(side=tk.RIGHT)

def poke():
    label_text.set("Ow!")

button = tk.Button(frame, text="Poke", command=poke)
button.pack(side=tk.LEFT)

window.mainloop()

Ważne elementy:

  • Dodanie ramki w oknie, która pozwala na pozycjonowanie
  • Etykieta i przycisk są umieszczone w ramce
  • Etykieta i przycisk podczas pakowania ustalają swoją pozycję

Pole tekstowe

import tkinter as tk

window = tk.Tk()

frame = tk.Frame(window)
frame.pack(fill="x")

entry_text = tk.StringVar()
entry = tk.Entry(frame, textvariable=entry_text)
entry.pack(side=tk.TOP)

label_text = tk.StringVar()
label_text.set("Hello")

label = tk.Label(frame, textvariable=label_text)
label.pack(side=tk.RIGHT)

def poke():
   label_text.set(entry_text.get())

button = tk.Button(frame, text="Poke", command=poke)
button.pack(side=tk.LEFT)

window.mainloop()

Ważne elementy:

  • Tworzymy obiekt typu Entry i wstawiamy go do ramki - obiekt używa zmiennej do przechowywania tekstu
  • Po naciśnięciu przycisku, wyciągamy tekst ze zmiennej związanej z polem tekstowym i zapisujemy do zmiennej związanej z etykietą

Okienka

Inne widgety:

  • Button
  • Canvas
  • Checkbutton
  • Entry
  • Frame
  • Label
  • Listbox
  • Menu
  • Menubutton
  • Message
  • Radiobutton
  • Scale
  • Scrollbar
  • Text
  • LabelFrame
  • PanedWindow
  • Spinbox

http://effbot.org/tkinterbook/tkinter-classes.htm

Zadanie

Napisz interfejs graficzny dla listy kontaktów firmy Apples & Oranges, który pozwoli na wstawianie nowych pracowników.

Teraz na pewno już czas żeby włączyć PyCharma?

import tkinter as tk
window = tk.Tk()

def make_row(parent, caption):
    frame = tk.Frame(parent).pack(fill="x")

    tk.Label(frame, text=caption).pack(side=tk.LEFT)

    entry_text = tk.StringVar()
    tk.Entry(frame, textvariable=entry_text).pack(side=tk.RIGHT)

    return entry_text

data = {}
for row in ["ID", "Name", "Surname", "Telephone", "Email", "Employee Type", "Language/Plant"]:
    data[row] = make_row(window, row + ":")

employees = Employees()

def add_employee():
    if data["Employee Type"].get() == "Programmer":
        employee = Programmer(int(data["ID"].get()), data["Name"].get(), data["Surname"].get(),
                      data["Telephone"].get(), data["Email"].get(), data["Language/Plant"].get())
        employees.add(employee)

    if data["Employee Type"].get() == "Planter":
        employee = Planter(int(data["ID"].get()), data["Name"].get(), data["Surname"].get(),
                      data["Telephone"].get(), data["Email"].get(), data["Language/Plant"].get())
        employees.add(employee)

    employees.print_all()

frame = tk.Frame(window)
frame.pack(fill="x")
tk.Button(frame, text="Add", command=add_employee).pack(side=tk.BOTTOM)

window.mainloop()

Co dalej?

Czego jeszcze musisz się nauczyć?

Ale jak?

FIN

Konrad Siek <konrad.siek@cs.put.edu.pl>

http://www.cs.put.poznan.pl/ksiek
https://github.com/kondziu
https://twitter.com/kondziu

KN SKiSR: https://dsg.cs.put.poznan.pl/wiki/

SpaceForward
Right, Down, Page DownNext slide
Left, Up, Page UpPrevious slide
POpen presenter console
HToggle this help