IB111 Základy programování Radek Pelánek 2018 1/59 Rozcvička O Řešením je identifikace místa poblíž Brna. Pokud je právě 6. 11. 2009, najdete na tomto^niístě djalšT zrjráyu stručný úvod pro potreby DU Proč? • vstupní data • uložení výstupu programu • zachovaní „stavu" programu mezi jednotlivými běhy větší projekty: databáze základní postup: • otevření souboru • práce se souborem (čtení / zápis) • zavření souboru 4/59 Práce se soubory: otevření, uzavření • f = open(filename, mode) • jméno souboru: řetězec • způsob otevření: • ctení ("r") • zápis ("w") - přepíše soubor, pokud není, vytvoří jej • přidání na konec ("a") 9 další možnosti: čtení i zápis, binární režim • uzavření: f .closeO 5/59 Práce se soubory: iterování po rádcích for line in f.readlines(): print(line) Alternativní způsob: for line in f: print(line) více priste 4 □ ► < SI ► Objekty: Motivace udržování dat pohromadě • záznamy o knihách • název knihy, autor, ISBN, rok vydání • postava v počítačové hře • souřadnice (x, y), energie, vybavení, seznamy, n-tice book = ["Godel, Escher, Bach: An Eternal Golden Braid", "Douglas R. Hofstadter", "978-0465026562", 1999] print(book[0]) # title print(book[1]) # author nepojmenované - nutno si pamatovat poradí položek (v hlavě, v komentářích, v konstantách) toto není pěkné 8/59 TITLE = O AUTHOR = 1 book = ["Godel, Escher, Bach: An Eternal Golden Braid", "Douglas R. Hofstadter", "978-0465026562", 1999] print(book[TITLE]) print(book[AUTHOR]) lepší jak předchozí, ale pořád škaredé slovníky - pojmenované položky book = {"title": "Godel, Escher, Bach: An Eternal Golden "author": "Douglas R. Hofstadter", "isbn": "978-0465026562", "year": 1999} print(book["title"]) print(book["author"]) toto není špatné, ale ... 10/59 Motivace II • vlastní datové typy: nejen data, ale i funkcionalita • schovávání škaredých detailů Motivace II • vlastní datové typy: nejen data, ale i funkcionalita • schovávání škaredých detailů příklad: dvojrozměrné matice z přednášky o datových typech • použitá reprezentace: • reprezentovány pomocí seznamu seznamů • nepěkný způsob zjišťování velikosti matice • co bychom chtěli? • spolu s maticí si udržovat informace o její velikosti • mít něco, co kontroluje přístupy do matice 11/59 Motivace III • objektům se nelze vyhnout (v Pythonu a jazyků) • zápis volání funkcí pres operátor „tečka" o řetězce, seznamy, slovníky, soubory, ... Nezbytné využití objektů t = "hello" print(t.upper()) s = [7, 14, 42, 0] s.sort() s.append(9) d = {"a": 1, "b": 2} s = d.keysO f = open("myfile.txt") line = f .readlineO Složené datové typy Záznamy, struktury • datový typ složený z více položek • typicky fixní počet položek, deklarované typy • C: struct • Pascal: record Objekty • často rozšíření struktur • kombinují data a funkce (metody) • C++, Java, Python: class Objektově orientované programování varovaní Toto není objektově orientované programování. • zde probíráme základy využití objektů v Pythonu • primárně náhrada za záznamy/struktury • jednoduché využití metod • „objektově orientované programování" je podstatně • zapouzdření, veřejné/soukromé atributy • dědičnost • polymorfismus • metodika návrhu programů • viz navazující kurzy ozitejsi 15/59 Objekty v Pythonu class Person: def __init_ self.name self.age = age (self, name, age) = name def say_hello(self): print (self .name + 11 says hello.") def rename_to(self, new_name): print(self.name + " renamed to " + new_name + ".") self.name = new name 16/59 Slovníček pojm (zjednodušeně) o U třída objekt atribut obecný uživatelsky definovaný typ, charakterizován atributy konkrétní instance třídy „to co je za tečkou", atributy jsou datové a funkční datový atribut proměnná patřící třídě/objektu (*) metoda funkce, která je vázaná na danou třídu konstruktor inicializační metoda, vytváří objekt (*) je rozdíl mezi atributem třídy a objektu, více později 1 Pojmy - intuitivní ilustrace • třída: Pes • objekt (instance): Alík • datové atributy: rasa, jméno, věk, poloha • metody: štěkej, popoběhni 18/59 kty v Pythonu • definice třídy: class MyObject: • definice metod: def do_something(self, parameter): • self - povinný první parametr • odkaz na aktuální objekt • self není klíčovým slovem, jen silnou konvencí • objekty • instance třídy, vlastní atributy • přístup k atributům pomocí tečkové notace Vyt vorem o bjekt u • speciální metoda (konstruktor):__init o především inicializace atributů class Person: def __init__(self, name, age): self.name = name self.age = age vytvoření objektu: homer = Person("Homer Simpson", 34) print(homer.name) print(homer.age) 20/59 • tečková notace • objekt před tečkou se předá jako první parametr (self) class Person: • • • def say_hello(self): print (self .name + 11 says hello.") homer.say_hello() # Homer Simpson says hello. 21/59 Metody a modifikace objektu class Person: def rename_to(self, new_name): print (self .name + 11 renamed to 11 + new_name self.name = new name homer.rename_to("Homer Jay Simpson") # Homer Simpson renamed to Homer Jay Simpson. print(homer.name) # Homer Jay Simpson Prístup k atributům • přímý: 9 přistupujeme přímo k atributu, můžeme i měnit • homer.name • nepřímý: • pomocí metod („getters and setters") • get_name, set_name Který zvolit? • závisí na použití • objekt jen pro držení dat: přímý přístup je nejspíše OK • schováváme v objektu složitější vnitřnosti: pište metody PEP8 konvence: názvy • jména tříd: CapWords (velká počáteční písmena slov, bez oddělovačů slov) • atributy (datové, metody): stejně jako běžné proměnné/funkce lowercase, lower_case_with_underscores 24/59 • knihy • studenti • čas • matice • rodokmen • textové obrázky 25/59 • práce se seznamem knih • kniha má: název, autora, ISBN • chceme seznam načítat/ukládat do souborů 26/59 Knihy class Book: def __init__(self, title, author, isbn): self.title = title self.author = author self.isbn = isbn geb = Book("Godel, Escher, Bach", MHofstadterM, "978-0465026562M) neverwhere = BookO'Neverwhere", "Gaiman", ,,978-0380789016M) print(neverwhere.author) 27/59 nihy: načítania ukladaní def load_library(filename): book_list = [] with open(filename, MrM) as f: for line in f: a, t, i = line. split (11;11) book_list.append(Book(t, a, return book_list def save_library(filename, book_list): with open(filename, MwM) as f: for book in book_list: f. write (book, title + 11;11 + book, author + 11;11 + book.isbn + M\nM) < □ ► < si ► save_library("library.csv", [geb, neverwhere]) books = load_library("library.csv") for b in books: print(b.title) vytvořený / načítaný soubor library.csv: Godel, Escher, Bach;Hofstadter;978-0465026562 Neverwhere;Gaiman;978-0380789016 29/59 CSV jsou super, ty chcete! • CSV = Comma-separated values • formát pro reprezentaci tabulkových dat • jednoduchý textový formát, položky odděleny čárkami (nebo něčím podobným) 9 vlastně ne úplně jednoduchý a pro reálné aplikace chcete použít knihovny jako je csv nebo pandas, každopádně CSV jsou super, viz též https://xkcd.com/1301/ 30/59 Príklad: studenti a kurzy • reprezentace studentů a kurzů • kurz má seznam zapsaných studentů class Student: def __init__(self, uco, name) self.uco = uco self.name = name 31/59 Príklad: studenti a kurzy class Course: def __init__(self, code) self.code = code self . students = [] def add_student(self, student): self.students.append(student) def print_students(self): i = 1 for s in self.students: print (str(i) + 11.11, str(s.uco), s.name, sep=M\tM) i += 1 Studenti a kurzy: použití jimmy = Student(555007, "James Bond") iblll = Course("IB111") iblll.add_student(Student(555000, "Luke Skywalker")) ibl11.add_student(j immy) iblll.add_student(Student(555555, "Bart Simpson")) iblll.print_students() # 1. 555000 Luke Skywalker # 2. 555007 James Bond # 3. 555555 Bart Simpson 33/59 Studenti a kurzy: řazení Co když chceme seřazený seznam studentů? • přirozené sorted(self . students) nefunguje - není definováno uspořádání na studentech a možnosti: • definovat uspořádání na studentech - metoda __It__(self, other) • sorted(self.students, key=lambda s: s.name) o použít pomocný seznam a ten seřadit: tmp = [(s.name, s.uco) for s in self.students] • co uspořádání abecedně podle příjmení? 34/59 35/59 Kahoot kod: otazky 2, 3, 4, 5 class Person: def __init__(self, name): self.name = name self.best_friend = None def set_friend(self, person): self.best_friend = person alice = PersonC'Alice") bob = PersonO'Bob") cyril = PersonC'Cyril") alice.set_friend(bob) bob.set_friend(cyril) 36/59 Python Tutor vizualizace Frames Objects Person dass hide attributes _init_ function _init_(self, name) set_friend function set_friend(self, person) erson instance friend me "Alice1 Person instance est friend name 1 BobT Person instance best friend name class Point: def __init__(self, x, y): self.x = x self.y = y def shift(self, x, y): self.x += x self.y += y a = Point(3, 2) b = Point(1, 4) points = [a, b, Point(0, 2)] < if? ► < = > < -ž ► -s -o°so 38/59 Příklad: reprezentace času (jednoduchá naivní verze, pořádně: knihovna datetime) class Time: def __init__(self, h, m, s): self.hours = h self.minutes = m self.seconds = s self.validate() def validate(self): if self.seconds >= 60: self.minutes += self.seconds // 60 self .seconds = self .seconds °/0 60 if self.minutes >= 60: self.hours += self.minutes // 60 self .minutes = self .minutes °/0 60 Reprezentace času class Time: # ... continued ... def add_seconds(self, sec): self.seconds += sec self.validate() def pretty_print(self): print ("{}:{: 02}: {: 02}11. format (self . hours, self.minutes, self.seconds)) t = Timed, 30, 72) t.pretty_print() t.add_seconds(107) t.pretty_print() 40/59 Príklad: matice oproti dřívější ukázce práce s maticemi: • chceme uchovávat i jejich velikost • chceme bezpečný přístup k prvkům class Matrix: def __init__(self, rows, cols): self.rows = rows self.cols = cols self.matrix = [[0 for i in range(self.cols)] for i in range(self.rows)] 41/59 atice: metody class Matrix: # ... continued ... def check(self, row, col): if row < 0 or row >= self.rows: print("Bad row index.") return False if col < 0 or col >= self.cols: print("Bad column index.") return False return True def get(self, row, col): if self.check(row, col): return self.matrix[row][col] def set(self, row, col, value): if self.check(row, col): self .matrix [row] [col] *= VcElue Matice: násobení def matrix_mult(matL, matR): if matL.cols != matR.rows: print (11 Incompatible matrices .11) return result = Matrix(matL.rows, matR.cols) for k in range(matL.cols): result.set(i, j, result.get(i, j) + matL get(i, k) * matR.get(k, j)) for i in range(matL.rows): for j in range(matR.cols): return result 43/59 Matice: poznámky čistě ilustrativní příklad reálnější verze by využívala: • přetížení operátoru krát: • výjimky, příp. as ser t • ... a nebo rovnou numpy Príklad: rodokmen • rozšírime objekt pro osoby o rodičovské vztahy • pro jednoduchost jen jeden rodič • uvažujme jen ženy - Mater čerta, pater incertus est. • rodokmen ^ strom potomkyň • pro každou ženu si pamatujeme: ■ ✓ • jmeno • matka • seznam dcer • matka, seznam dcer - odkazy na objekty (abychom s nimi mohli pracovat), nikoliv „jména" 45/59 class Woman: def __init__(self, name): self.name = name self.mother = None self . daughters = [] def add_daughter(self, daughter): self.daughters.append(daughter) daughter.mother = self alice = Woman("Alice") mary = Woman("Mary") lisa = Woman("Lisa") carol = Woman("Carol") alice.add_daughter(mary) alice.add_daughter(lisa) mary.add_daughter(carol) - Základní použití print(carol.mother.name) print(alice.mother.name) print(len(lisa.mother.daughters)) # Mary # error message # 2 def siblings(a, b): return a.mother != None and a.mother == b.mother print(siblings(mary, alice)) print(siblings(mary, lisa)) # False # True 47/59 Výpis rodokmenu Chceme vypsat textově znázorněný rodokmen - odsazení podle generací: Alice Mary Lisa Carol Betty L Maria Jak to udělat? 48/59 Výpis rodokmenu Rekurze! def draw_family_tree(woman, level=0): print((" 11 * level) + woman.name) for daughter in woman.daughters: draw_family_tree(daughter, level + 1) 5 vT) Q, O 49/59 Rodokmen: nejstaršř predkyně Rekurzivně: def oldest_ancestor(woman): if woman.mother == None: return woman return oldest_ancestor(woman.mother) Iterativně: def oldest_ancestor2(woman): while woman.mother != None: woman = woman.mother return woman 50/59 Rodokmen: počet potomkyň Chceme vypočítat počet všech potomkyň (i nepřímých). Rodokmen: počet potomkyň Chceme vypočítat počet všech potomkyň (i nepřímých). rekurze s návratovou hodnotou: • funkce count_of f springO vrací počet potomkyň • hodnotu vypočítáme pomocí volání count_of f springO na dcerách 52/59 Rodokmen: počet potomkyň def count_offspring(woman): count = 0 for daughter in woman.daughters: count += 1 + count_offspring(daughter) return count Pozn. count_off spring by též mohla být metoda třídy Woman Textové obrázky • objekt reprezentující obrázek • metody: přidej bod, přidej čtverec, zkombinuj s jiným obrázkem, ... 9 „textové" vykreslení demo ukázka programování 54/59 Varování: Statické atributy • definovány přímo ve třídě • patří samotné třídě, ne objektům • mají svůj smysl, ale v tuto chvíli spíše zdroj chyb • v tomto předmětu raději nepoužívejte class MyClass: x = 0 def __init. self .y _(self, n) = n print(MyClass.x) my_object = MyClass(17) print(my_obj ect.y) print(my_obj ect.x) # 0 # 17 # 0 (same as MyClass.x) 55/59 Statické atributy: ilustrace problému class Person: hobbies = [] def __init__(self, name): self.name = name def add_hobby(self, hobby): self.hobbies.append(hobby) mirek = Person("Mirek Dusin") mirek. add_hobby (11 running") mirek.add_hobby("world peace") bidlo = Person("Dlouhé Bidlo") bidlo.add.hobby("alcohol") print(mirek.hobbies) Python Tutor vizualizace Python 3,6 class Person: hobbies = [] def _init_(self, name): self.name = name def add_hobby(self, hobby): self.hobbies.append(hobby) 7 mirek = Person("Mi rek Dusin") mi rek.add_hobby("runni ng") mirek.addhobby("world peace") bidlo = Person("Dlouhe Bidlo") »12 bidlo. add_hobby("alkohDl")_ Edit code I Live programming is just executed execute de to set a breakpoint; use the Back and Forward buttons to jump there. Frames Global frame Person • Object; Person class hide attributes _init_ function _init_(self, name) add_hobby function add hobby(self, hobby) hobbies list 0 l 2 "runni ng" "world peace" "alkohol" Person instance name | "Mi rek Duši n" Person instance name "Dlouhé Bidlo" 57/59 Varování: kopírování objektů Co udělá? a = Matrix(2, 2) a. set(0, 1, 6) b = a b. set(0, 0, 7) print(a.matrix) Vytvoření aliasu, nikoliv kopírování (podobně jako u seznamů) Jak kopírovat? • knihovna copy: copy (mělká kopie), deepcopy (hluboká kopie) • vytvořit speciální kopírovací konstruktor Záznamy/struktury • používáme pro seskupení souvisejících dat • v Pythonu přímo nejsou Objekty • složitější než záznamy • data + metody • v tomto předmětu je používáme primárně jako záznamy s jednoduchými metodami • více o objektově orientovaném programování (dědičnost, polymorfismus, objektový návrh) v navazujících předmětech 59/59