Proměnné, paměť, soubory IB111 Základy programování Radek Pelánek 2017 1 / 54 Rozcvička I a = [3, 1, 7] print(sorted(a)) print(a) b = [4, 3, 1] print(b.sort()) print(b) 2 / 54 Rozcvička II a = ["magic"] a.append(a) print(a[1][1][1][1][1][0][1]) 3 / 54 Dnes „důležité technické detaily proměnné podrobněji reprezentace dat v paměti práce se soubory základní témata obecně důležitá detaily specifické pro Python 4 / 54 Python Tutor – vizualizace Vizualizace použité ve slidech: http://www.pythontutor.com Doporučeno projít si interaktivně. 5 / 54 Proměnné podrobněji globální a lokální proměnné proměnné a paměť kopírování předávání parametrů funkcím 6 / 54 Názvy proměnných – konvence konstanty – velkými písmeny běžné proměnné: smysluplná slova víceslovné názvy: lower case with underscores krátké (jednopísmenné) názvy: indexy souřadnice: x, y pomocné proměnné s velmi lokálním využitím 7 / 54 Globální a lokální proměnné Globální proměnné definovány globálně (tj. ne uvnitř funkce) jsou viditelné kdekoli v programu Lokální proměnné definovány uvnitř funkce jsou viditelné jen ve své funkci 8 / 54 Rozsah proměnných obecněji proměnné jsou viditelné v rámci svého „rozsahu rozsahem mohou být: funkce moduly (soubory se zdrojovým kódem) třídy (o těch se dozvíme později) a jiné (závisí na konkrétním jazyce) relevantní terminologie: „namespace , „scope 9 / 54 Globální a lokální proměnné a = "This is global." def example1(): b = "This is local." print(a) print(b) example1() # This is global. # This is local. print(a) # This is global. print(b) # ERROR! # NameError: name ’b’ is not defined 10 / 54 Globální a lokální proměnné 11 / 54 Globální a lokální proměnné vytváříme novou lokální proměnnou, neměníme tu globální —— a = "Think global." def example2(): a = "Act local." print(a) print(a) # Think global. example2() # Act local. print(a) # Think global. 12 / 54 Globální a lokální proměnné 13 / 54 Globální a lokální proměnné Jak měnit globální proměnné? —— a = "Think global." def example3(): global a a = "Act local." print(a) print(a) # Think global. example3() # Act local. print(a) # Act local. 14 / 54 Lokální proměnné: deklarace lokální proměnná vzniká, pokud je přiřazení kdekoli uvnitř funkce —— a = "Think global." def example4(change_opinion=False): print(a) if change_opinion: a = "Act local." print("Changed opinion:", a) print(a) # Think global. example4() # ERROR! # UnboundLocalError: local variable ’a’ referenced bef # assignment 15 / 54 Rozsah proměnných: for cyklus rozsah proměnné v Pythonu není pro „dílčí blok kódu , ale pro celou funkci (resp. globální kód) častá chyba (záludný překlep): proměnná for cyklu použita po ukončení cyklu —— n = 9 for i in range(n): print(i) if i % 2 == 0: print("I like even length lists") 16 / 54 Globální a lokální proměnné proměnné interně uloženy ve slovníku výpis: globals(), locals() —— def function(): x = 100 s = "dog" print(locals()) a = [1, 2, 3] x = 200 function() print(globals()) 17 / 54 Globální a lokální proměnné Doporučení: vyhýbat se globálním proměnným pouze ve specifických případech, např. globální konstanty Proč? horší čitelnost kódu náročnější testování zdroj chyb obecně: „lokalita kódu je užitečná 18 / 54 Globální proměnné: alternativy předávání parametrů funkcím a vracení hodnot z funkcí objekty (probereme později) a další (nad rámec předmětu): statické proměnné (C, C++, Java, ...), návrhový vzor Singleton, . . . 19 / 54 Funkce Připomenutí z dřívější přednášky vstupy (parametry) funkce návratová hodnota vedlejší efekty return výpisy změny globálních proměnných 20 / 54 Funkce bez vedlejších efektů „čistá funkce = funkce bez vedlejších efektů Reklama Čisté funkce jsou vaši přátelé! ladění modularita přemýšlení o problému čitelnost kódu 21 / 54 Funkce: vedlejší efekty změna měnitelných parametrů OK, ale nemíchat s návratovou hodnotou, vhodně pojmenovat, dokumentovat změna globálních proměnných (které nejsou parametry) většinou cesta do pekla změna stavu systému (libovolné „výpisy , zápis do souboru, databáze, odeslání na tiskárnu, . . . ) nutnost, ale nemíchat chaoticky s výpočty 22 / 54 Proměnné a paměť Proměnné v různých jazycích pojmenované místo v paměti odkaz na místo v paměti (Python) kombinace obou možností Přiřazení proměnné ve stylu C: změna obsahu paměti proměnné ve stylu Pythonu: změna odkazu na jiné místo v paměti 23 / 54 Proměnné a paměť 24 / 54 Proměnné a paměť funkce id() – vrací „identitu objektu (± adresa v paměti) —— a = 1000 b = a print(a, b) print(id(a), id(b)) b += 1 print(a, b) print(id(a), id(b)) a = [1] b = a print(a, b) print(id(a), id(b)) b.append(2) print(a, b) print(id(a), id(b)) 25 / 54 Rovnost vs. „stejná identita a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True print(id(a) == id(b)) # False —— operátor is – stejná identita 26 / 54 Předávání parametrů funkcím hodnotou (call by value) předá se hodnota proměnné (kopie) standardní v C, C++, apod. odkazem (call by reference) předá se odkaz na proměnnou lze použít v C++ jiné možnosti (jménem, hodnotou-výsledkem, . . . ) jazyk Python: něco mezi voláním hodnotou a referencí podobně funguje např. Java někdy nazýváno call by object sharing 27 / 54 Předávání parametrů funkcím Předávání parametrů hodnotou parametr je vlastně lokální proměnná funkce má svou vlastní lokální kopii předané hodnoty funkce nemůže změnit hodnotu předané proměnné Předávání parametrů odkazem nepředává se hodnota, ale odkaz na proměnnou změny parametru jsou ve skutečnosti změny předané proměnné 28 / 54 Předávání parametrů: příklad v C++ #include void test(int a, int& b) { a = a + 1; b = b + 1; } int main() { int a = 1; int b = 1; std::cout << "a: " << a << ", b: " << b << "\n"; test(a, b); std::cout << "a: " << a << ", b: " << b << "\n"; } 29 / 54 Předávání parametrů v Pythonu Předávání parametrů v Pythonu paramater drží odkaz na předanou proměnnou změna parametru změní i předanou proměnnou pro neměnitelné typy tedy v podstatě funguje jako předávání hodnotou čísla, řetězce, ntice (tuples) pro měnitelné typy jako předávání odkazem pozor: přiřazení znamená změnu odkazu Připomenutí: neměnitelné typy: int, str, tuple, . . . měnitelné typy: list, dict, . . . 30 / 54 Předávání parametrů: příklad def change_list(alist, value): alist.append(value) def return_new_list(alist, value): newlist = alist[:] newlist.append(value) return newlist 31 / 54 Předávání parametrů: příklad II def test(s): s.append(3) s = [42, 17] s.append(9) print(s) t = [1, 2] test(t) print(t) 32 / 54 Předávání parametrů: vizualizace 33 / 54 Předávání parametrů: příklad III Operátor += různé chování pro neměnné typy a pro seznamy —— def increment(x): print(x, id(x)) x += 1 print(x, id(x)) p = 42 increment(p) print(p, id(p)) def add_to_list(s): print(s, id(s)) s += [1] print(s, id(s)) t = [1, 2, 3] add_to_list(t) print(t, id(t)) 34 / 54 Předávání parametrů: příklad IV Pozor na rozdíl mezi = a += u seznamů —— def add_to_list1(s): print(s, id(s)) s += [1] print(s, id(s)) t = [1, 2, 3] add_to_list1(t) print(t) # [1, 2, 3, 1] def add_to_list2(s): print(s, id(s)) s = s + [1] print(s, id(s)) t = [1, 2, 3] add_to_list2(t) print(t) # ??? 35 / 54 Předávání parametrů: vizualizace 36 / 54 Předávání výsledků funkcí funkce mají: libovolný počet parametrů právě jeden výstup (return x) Co když chceme z funkce vrátit více hodnot? Elementární příklad: dělení se zbytkem 37 / 54 Předávání více výsledků Jak předat více výsledků? n-tice (možno zapisovat i bez závorek) —— def division_with_remainder(a, b): return a // b, a % b div, mod = division_with_remainder(23, 4) 38 / 54 Předávání více výsledků Jak předat více výsledků? slovník („pojmenované výstupy) —— def division_with_remainder(a, b): return {"div": a // b, "mod": a % b} result = division_with_remainder(23, 4) print(result["div"], result["mod"]) 39 / 54 Kopírování objektů Vytvoření aliasu b = a odkaz na stejnou věc Mělká kopie b = a[:] nebo b = list(a) vytváříme nový seznam, ale prvky tohoto seznamu jsou aliasy obecně i pro jiné typy než seznamy (knihovna copy) b = copy.copy(a) Hluboká kopie kompletní kopie všech dat obecné řešení (opět knihovna copy) b = copy.deepcopy(a) 40 / 54 Kopírování objektů listA listB [3, 5] [2, 9] [1, 7] [3, 5] [2, 9] [1, 7] listA listB [3, 5] [2, 9] [1, 7] mělká kopie hluboká kopie 41 / 54 Správa paměti Různé přístupy ke správě paměti manuální – funkce pro přidělení/uvolnění paměti automatická – paměť je uvolněna na konci života proměnné automatická – počítání referencí kolik částí programu ještě s danou pamětí pracuje pokud už nikdo, paměť je uvolněna automatická – garbage collection jednou za čas se uklidí nepoužívaná paměť Správa paměti v Pythonu automatická – počítání referencí + někdy i větší úklid počet referencí sys.getrefcount(object) 42 / 54 Práce se soubory Proč? vstupní data uložení výstupu programu zachování „stavu programu mezi jednotlivými běhy větší projekty: databáze 43 / 54 Práce se soubory základní postup: otevření souboru práce se souborem (čtení / zápis) zavření souboru 44 / 54 Práce se soubory: otevření, uzavření f = open(filename, mode) jméno souboru: řetězec způsob otevření: čtení ("r") zápis ("w") – přepíše soubor, pokud není, vytvoří jej přidání na konec ("a") další možnosti: čtení i zápis, binární režim uzavření: f.close() 45 / 54 Práce se soubory: čtení, zápis f.write(text) – zapíše řetězec do souboru neukončuje řádky, je třeba explicitně použít ’\n’ f.readline() – přečte jeden řádek f.readlines() – přečte všechny řádky, vrací seznam řádků f.read(count) – přečte daný počet znaků f.read() – přečte celý soubor, vrací řetězec f.tell() – aktuální pozice v souboru f.seek(position) – přesun pozice v souboru 46 / 54 Práce se soubory: iterování po řádcích for line in f.readlines(): print(line) —— Alternativní způsob: for line in my_file: print(line) 47 / 54 Frekvence slov v souboru def word_freq_file(filename): f = open(filename) text = "" for line in f.readlines(): text += line output_word_freq(text) # -> minule f.close() word_freq_file("devatero_pohadek.txt") 48 / 54 Frekvence slov v souboru: úkol Upravte výpis, aby vypisoval nejčastější slova zadané minimání délky. a 2426 se 1231 na 792 to 781 v 468 že 467 je 446 si 401 tak 384 do 365 jsem 287 jako 284 když 281 řekl 251 nebo 120 ještě 110 pane 109 toho 91 povídá 83 král 80 49 / 54 Práce se soubory: with Speciální blok with není třeba soubor zavírat (uzavře se automaticky po ukončení bloku) —— with open("/tmp/my_file", "r") as my_file: lines = my_file.readlines() print(lines) 50 / 54 Načítání vstupu od uživatele: input x = input("Give me a large number:") x = int(x) print("My number is larger:", x+1) —— input vrací řetězec typicky nutno přetypovat Python2: odlišné chování (input, raw input) 51 / 54 Výjimky Co když uživatel zadá místo čísla "deset"? Co když soubor neexistuje? častý přístup: doufat, že se to nestane základní přístup: důsledně před každou operací kontrolovat vstupy u složitějších programů nepřehledné sofistikovanější přístup: výjimky 52 / 54 Výjimky try: x = input("Give me a large number:") x = int(x) print("My number is larger:", x+1) except ValueError: print("Sorry, that is not a valid number") —— Nad rámec tohoto kurzu. 53 / 54 Shrnutí představa o reprezentaci v paměti je potřeba parametry funkcí: měnitelné, neměnitelné mělká vs. hluboká kopie práce se soubory, vstupy 54 / 54