IB113 Radek Pelánek 2019 1/63 Dnešní prednáška • objekty a strukturovaná data - rychlý úvod • 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í, 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é 4/63 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 ... •<□► < [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 • mít něco, co kontroluje přístupy do matice 7/63 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 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 ozitejsi •<□► 4 ^ t < -ž ► < -š ► 11/63 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/63 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 1 Pojmy - intuitivní ilustrace • třída: Pes • objekt (instance): Alík • datové atributy: rasa, jméno, věk, poloha • metody: štěkej, popoběhni 15/63 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 Vytvoření objektu • 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/63 • 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. < □ ► < [fpi ► < b ► < ^ ► 1 -0 0,0 18/63 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 21/63 • knihy • studenti • čas • matice • rodokmen • textové obrázky 22/63 • práce se seznamem knih • kniha má: název, autora, ISBN • chceme seznam načítat/ukládat do souborů 23/63 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) 24/63 Knihy: načítania ukládání 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/63 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/63 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/63 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/63 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/63 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/63 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/63 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] '= value 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/63 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 37/63 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) 38/63 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 None name "Cyril" 39/63 Kahoot kód: otázky 9, 10, 11, 12 class Point: def __init. self x self.y (self, x, y) x 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)] •<□► < [SP ► < -ž ► < ► š s) c\cy 40/63 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í 41/63 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 .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) 42/63 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 = { ii ~ ii 1, "b": 2} s = d.keys() f = openC'myf ile. txt") line = f .readlineO •<□► 4 ^ >■ < -ž ► < -š ► 45/63 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ů •<□► 4 ^ t < -ž ► < -š ► 46/63 Styl psaní programu Při programování jde nejen o korektnost a efektivitu, ale i čitelnost a čistotu kódu: 9 snadnost vývoje, testování • údržba kódu • spolupráce s ostatními (sám se sebou po půl roce) 47/63 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ů 48/63 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 49/63 Komentované ukázky kódů • ř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 big_frame(word_list), která pro zadaný seznam slov word_list vypíše tato slova každé na jeden řádek a kolem nich udělá rámeček ze znaku '+'. def frame(text, symbol): print(symbol*(len(text)+2)) print(symbol + text + symbol) print(symbol*(len(text)+2)) 51/63 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 ) ) 53/63 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 •<□► 4 ^ t < -ž ► < -š ► 54/63 aximá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 i •o^o 55/63 max_pair_sum(num_list) count=0 max=0 sum=0 sudy=0 for number in num_list sum+=number count+=l if count°/02==l: lichy=number if count0/o2==0: sudy=number if sum > max: max=sum if count0/o2==0: sum=sum-lichy if count°/02==l: sum=sum-sudy return max 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,z5u5q,p)) 57/63 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 58/63 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 60/63 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]) •<□► 4 ^ t < *■ 4 S ► 62/63 • 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? š s) c\cy 63/63 • 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 64/63