IB113 Radek Pelánek 2021 1/60 Dnešní prednáška • objekty a strukturovaná data - rychlý úvod 9 ukázka na živo - objekty + opakování • styl, rozbor odevzdaných příkladů z Umíme programovat 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í, Jak reprezentovat? 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é Pokus 2: Využití konstant TITLE = 0 AUTHOR = 1 book = ["Godel, Escher, Bach: An "Douglas R. Hofstadter", "978-0465026562", 1999] print(book[TITLE]) print(book[AUTHOR]) lepší jak předchozí, ale porád škaredé 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 ... •<□► < [SP ► < -ž ► < -e ► 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 • kontrolovat přístupy do matice 7/60 Motivace III • objektům se nelze vyhnout (v Pythonu a jazyků) • zápis volání funkcí přes 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 Proč t.upper() a ne upper(t)? Bez povědomí o objektech to nedává smysl. 9/60 Záznamy, struktury • datový typ složený z více položek 9 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 10/60 ektově 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 (Java, C++) ozitejsi •f) c\ ry 11/60 rovně porozumění objektům O pochopení základních konceptů, porozumění vestavěným typům O použití objektů jako záznamy/struktury (shlukování dat) O využití objektů s metodami O plné objektově orientované programování v IB113 pouze bod 1. body 2. a 3. lze zvládnout samostudiem na bod 4. se určitě hodí kurz 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 + 11 renamed to 11 + new_name + 11.") self.name = new name 13/60 Slovníček pojmů (zjednodušeně) 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 Poimv - intuitivní ilustrace • třída: pes • objekt (instance): Alík • datové atributy: rasa, jméno, věk, poloha • metody: štěkej, popoběhni 15/60 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 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) 17/60 Metody: definice a použití 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. 18/60 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ý: • 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 21/60 • knihy • studenti o čas • matice • rodokmen o textové obrázky 22/60 • práce se seznamem knih • kniha má: název, autora, ISBN • chceme seznam načítat/ukládat do souborů 23/60 Knihy class Book: def __init__(self, title, author, isbn): self.title = title self.author = author self.isbn = isbn geb = Book("Godel, Escher, Bach", MHofstadter", "978-0465026562M) neverwhere = BookO'Neverwhere", "Gaiman", ,,978-0380789016M) print(neverwhere.author) 24/60 Knihy: načítania ukladaní def load_library(filename): book_list = [] with open(filename, "r") as f: for line in f: a, t, i = line. split (";11) book_list.append(Book(t, a, i)) return book_list def save_library(filename, book_list): with open(filename, "w") as f: for book in book_list: f. write (book, title + 11;11 + book, author + 11;11 + book.isbn + "\n") 25/60 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 26/60 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 27/60 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="\t") i += 1 Studenti a kurzy: použití jimmy = Student(555007, "James Bond") ibll3 = Course("IB113") ibl13.add_student(Student(555000, "Luke Skywalker")) ibl13.add_student(j immy) ibl13.add_student(Student(555555, "Bart Simpson")) ib113.pr int.student s() # 1. 555000 Luke Skywalker # 2. 555007 James Bond # 3. 555555 Bart Simpson 29/60 Studenti a kurzy: razení 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í? 30/60 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() 32/60 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)] 33/60 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] Va"f ue 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 35/60 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 Textové obrázky • objekt reprezentující obrázek • metody: přidej bod, přidej čtverec, zkombinuj s jiným obrázkem, ... • „textové" vykreslení demo ukázka programování 37/60 Varování: Statické atributy • definovány přímo ve třídě • patří samotné třídě, ne objektům • mají svůj smysl, ale při začátcích spíše zdroj chyb class MyClass: x = 0 def __init__(self, n): self.y = 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) 38/60 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("drinking beer") print(mirek.hobbies) Objekty a globální proměnné příklad: herní plán (piškorky, dáma, apd) • přirozená reprezentace objektem, nahrazení globálních proměnných • datové atributy: velikost plánu, stav hracího plánu, figurky, ... • metody: vykresli, zanes tah, zkontroluj výhru, ... Vestavěné typy jako objekty 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 41/60 Dědičnost jeden z pokročilejších prvků objektového programování ilustrace myšlenky na příkladech: • typický „školní" příklad: geometrické objekty • praktický příklad: výpis heterogenních statistik z výukových systémů 42/60 Styl psaní programu Při programování jde nejen o korektnost a efektivitu, ale i čitelnost a čistotu kódu: o snadnost vývoje, testování • údržba kódu • spolupráce s ostatními (sám se sebou po půl roce) 43/60 Styl psaní programu obecná doporučení: • nepoužívat copy&paste kód • dekompozice na funkce, „funkce dělá jednu věc" • rozumná jména proměnných, funkcí specifická doporučení (závisí na programovacím jazyce, příp. společnosti) - důležitá hlavně konzistence: • odsazování • bílá místa (mezery v rámci řádku) • styl psaní víceslovných názvů •<□► 4 ^ t < -ž ► < -š ► 44/60 PEP8 - doporučení pro Python https://www.python.org/dev/peps/pep-0008/ • obecný „duch" doporučení celkem univerzální • částečně však specifické pro Python (pojmenování, bílá místa, ...) dnes na ukázkách, trochu více detailů na poslední přednášce 45/60 Komentované ukázky kódů o řešení z programátorských cvičení z umimeprogramovat.cz • automatické hodnocení, hodnotí pouze korektnost • ilustrované stylistické problémy ale časté i jinde upozornění: následující ukázky jsou všechny nějakým způsobem problematické Napište funkci frame(text, symbol), která vypíše text v rámečku tvořeném znakem symbol (předpokládejte, že symbol je řetězec tvořený právě jedním znakem). def frame(text, symbol): print(symbol*(len(text)+2)) print(symbol + text + symbol) print(symbol*(len(text)+2)) 47/60 Rámeček def frame(text, symbol): n=len(text) for i in range(n+2): print(symbol,end print() print(symbol+text+symbol) for i in range(n+2): print(symbol,end print() def frame(text, symbol): lenght = len(text) new_l = lenght +2 print CO" .format (symbol*new_l)) for i in range(0,new_l): if i == 0: print(symbol, end = elif i == new_l -1: print(symbol, end = print() else: print(text [i-1], end = '') print CO" .format (symbol*new_l)) _ i i _ i i ) ) 49/60 Součin nenulových Napište funkci nonzero_product(numbers), která pro zadaný seznam čísel numbers vrátí součin všech nenulových čísel v seznamu. def nonzero_product(numbers): součet = 1 for i in numbers: if i != 0: součet *= i return součet Maximální sousedi Napište funkci max_pair_sum(num_list), která pro zadaný seznam kladných čísel nurrUist vypočítá nejvyšší součet dvou po sobě jdoucích čísel. def max_pair_sum(num_list): new_list= [] for i in range(len(num_list)): if i max: max=sum if count0/o2==0: sum=sum-lichy if count°/02==l: sum=sum-sudy return max aximálnř sousedi def max_pair_sum(num_list): x=num_list[0]+num_list[1 y=num_list[1]+num_list[2 z=num_list[2]+num_list[3 u=num_list[3]+num_list[4 q=num_list[4]+num_list[5 p=num_list[5]+num_list[6 return(max(x, y, z, u, q, p)) 53/60 Napište funkci find_longest_word(words_list), která vrátí nejdelší slovo ze zadaného seznamu slov words_list. Pokud je nejdelších slov více, vrátí to z nich, které je v seznamu uvedeno jako první. def find_longest_word(words_list): a = 0 for i in words_list: if len(i) > a: a = len(i) b = i return b 54/60 Nejdelšŕ slovo v seznamu def find_longest_word(words_list): delka= [] for i in range(len(words_list)): délka.append(len(words_list [i])) for j in range(len(délka)): if délka[j]==max(délka): return words_list [j] Nejdelšŕ slovo v seznamu def find_longest_word(words_list): longest=words_list[0] long=len(words_list [0]) for i in range(len(words_list)): for j in words_list: if len(words_list[i])>long: long=len(words_list [i]) longest=words_list[i] else: continue return longest 56/60 Nejdelšŕ slovo v seznamu def find_longest_word(words_list) counter = 0 result = 11 11 for word in words_list: if len(word) > counter: counter = len(word) for word in words_list: if len(word) == counter: result = word break return result def find_longest_word(words_list): length = sorted(words_list, key=len) if len(length) == 1: return(length [0]) elif len(length [-2]) == len(length [-1]) and len(leng return(length [-2]) elif len(length [-3]) == len(length [-2]) == len(length return(length [-3]) else: return (length [-1]) 58/60 • Uveďte příklady dat, pro jejichž reprezentaci se přirozeně hodí objektový zápis. • Jaký je rozdíl mezi pojmy třída a objekt? • Jaký je význam pojmů atribut, metoda, konstruktor? • Co je to „metoda" a jak ji zapisujeme? • Jak přistupujeme k metodám a atributům objektu? • Jak vytvoříme objekt? • Co znamená self v zápisu metod? 59/60 • 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 • vestavěné typy používáme jako objekty • dobrý programátorský styl je důležitý příště: obrázky 60/60