Opakování Pythonu

Programování v GIS 2

Jan Caha

2025-02-24

Práce s Pythonem

  • cílem je naučit se rozumné návyky a postupy při programování v Pythonu
  • předcházet nevhodným postupům, návykům a chybám, které komplikují v dlouhodobém horizontu práci
  • i když některé postupy vypadají zbytečně složité či nepotřebné, většinou se vyplatí z dlouhodobého hlediska
  • většina postupů je založena na zkušenostech a doporučeních z praxe
  • “pokud jste nějaký kód neotevřeli 3 měsíce, mohl ho klidně napsat někdo cizí” - potřebujeme tedy kód, který je srozumitelný a použitelný i po delší době a pro někoho cizího
  • věci, které jsou “navíc” děláte pro svoje budoucí já a ulehčujete mu tím práci

AI

  • Github Copilot - umělá inteligence, která vám pomáhá psát kód (postup k získání na stránce předmětu)
  • výborná integrace do VS Code
  • využívá se pro:
    • doplňování kódu
    • návrhy funkcí a objektů
    • konzultace existujícího kódu
    • konzultace problémů
  • využití v tomto ohledu se doporučuje
  • neklade to menší nároky na programátora, možná dokonce naopak (vnímat omezení, potřeba kontrolovat výstupy)

Python

  • interpretovaný programovací (skriptovací) jazyk
  • dynamicky typovaný
  • široce využívaný - ať už samostatně, nebo i jako součást jiného softwaru
  • snadno propojitelný s dalšími knihovnami i softwarem
  • podpora jak objektového, tak i funkcionálního programování
  • verze 2.x už nepoužívat, verze 3 ideálně od 3.10 a novější (aktuální verze a cca dvě verze zpátky)

VS Code

  • IDE pro řadu jazyků a technologií
  • klíčové jsou extenze, které posunují funkcionalitu od textového editoru více k “težšímu” IDE
  • profily - pro různé technologie či využití
  • synchronizace po přihlášení
  • instalace - lze bez administrátorských práv

VS Code extenze pro Python

  • Python, Python Debugger
  • formátování kódu - Black Formatter, isort
  • kontroly kódu - autopep8, Flake8, Mypy Type Checker, Pylance, Pylint
  • generování docstringů - autoDocstring - Python Docstring Generator
  • Github Copilot - přihlášení, pouze pokud máte přístup (studenti mají) - nově existuje i limitovaná verze zdarma

Conda

  • systém pro správu Pythonu a jeho balíčků
  • použití z příkazové řádky - ale existuje i GUI (Anaconda)
  • oficiální a neoficiální kanály (conda-forge)
conda create -n pgis2 python=3.12
conda activate pgis2
conda config --add channels conda-forge
conda install GDAL
conda deactivate
  • v environmentu lze instalovat i skrze pip, ale nelze to doporučit - chybí návaznosti závislostí
  • v VS Code nutno nastavit interpretor jazyka

Instalace knihoven

conda activate pgis2
conda install black isort mypy pep8 flake8 pylint
conda install shapely fiona rasterio polars pandas geopandas
  • podle osnovy se máme věnovat pandas - polars je výrazně rychlejší implementace, novější, s lépe použitelnou syntaxí a čitelnějším kódem

Tvorba Conda prostředí

  • ideálně ne manuálně, ale z definičního souboru
  • yaml soubor s popisem prostředí (název, použité kanály, verze Pythonu, balíčky - klidně i s verzemi)
  • tvorba prostředí - conda env create -f env-pgis2.yaml - viz stránka předmětu
  • odstranění prostředí - conda env remove -n pgis2
  • je lepší prostředí odstranit, upravit definiční soubor a znovu vytvořit, než jej modifikovat manuálně (hrozí nekonzistence a nezaznamenání změn)

Python - použití jazyka
opakování

Proměnné

  • jednoduché - celá čísla, čísla s plovoucí desetinnou čárkou, text, pravdivostní hodnota
  • komplexní - seznam, slovník, tuple
seznam = [1, 2, 3]
slovnik = {"a": 1, "b": 2}
t = (1, 2, 3)
  • objekty - např. Path z knihovny pathlib
  • funkce - lze je také uložit do proměnné (trochu komplikované na představu)
def fun(): ...


a = fun
a()

Podmínky

  • konstrukce if, elif, else

Cykly

  • se známým počtem opakování, bez známého počtu opakování
  • konstrukce for a while
  • přeskočení iterace v cyklu continue a ukončení cyklu break
  • speciální případ for s enumerate() (iterace přes prvky seznamu, nebo slovníku a zároveň získání indexu)
  • range() - funkce pro generování číselné posloupnosti
v = ["a", "b", "c"]
for i, x in enumerate(v):
    print(f"Na pozici {i} je hodnota {x}")
Na pozici 0 je hodnota a
Na pozici 1 je hodnota b
Na pozici 2 je hodnota c

Seznamy - comprehensions

  • jednoduchá konstrukce na zpracování seznamu
vals = [1, 2, 3]
text = [f"cislo {x}" for x in vals]
print(text)
['cislo 1', 'cislo 2', 'cislo 3']
  • lze i filtrovat pomocí if
vals = [1, 2, 3, 5, 6, 7]
text = [str(x) for x in vals if x > 4]
print(text)
['5', '6', '7']
  • nahrazuje cyklus
  • vhodné pouze pro jednoduché operace a filtry, jinak může být nečitelné

Řetězce

  • často potřebujeme spojit řetězec s hodnotou, či podobná operace
  • konstrukce f"řetězec {promenna}" - tzv. f-string, vysoce optimalizovaný způsob spojování řetězců
  • jiné varianty nepoužívat
    • "hodnota {}".format(promenna)
    • "hodnota %s" % promenna
    • "hodnota " + str(promenna)
  • tvorba řetězce z pole
    • ", ".join(["a", "b", "c"])
    • ošetří poslední hodnotu a další limitní případy

Import knihoven či modulů

  • lze importovat objekty, funkce, moduly
  • lze buď celým názvem - import pathlib
  • lokální souboru lze i jako import .muj_soubor (doporučuje se pouze u jednoduchých skriptů, v případě modulů - knihoven lépe importovat plnou cestou)
  • lze importovat i s přejmenováním - import numpy as np
  • kombinace a variace importů občas ztěžují interpretaci kódu

Chyba

Počítače mají skvělou vlastnosti, dělají co je jim zadá. Počítače maji jeden zásadní problém, dělají přesně, co se jim zadá.

  • neřešitelná situace v programu
  • nelze dál pokračovat - program se zastaví
"a" + 1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 "a" + 1

TypeError: can only concatenate str (not "int") to str

Zpracování chyb

  • konstrukce raise
  • druhy chyb - dokumentace
  • konstrukce try, except, finally
try:
    "a" + 1
    print("Toto se nevykoná")
except TypeError as e:
    print(f"Chyba: {e}")
finally:
    print("Konec")
Chyba: can only concatenate str (not "int") to str
Konec

Magické proměnné či funkce

  • zapisují je jako __nazev__
  • odkazují na předpřipravené konstrukce (funkce, proměnné) v Pythonu a mají obvykle speciální chování
  • ukázka - tvorba objektu a funkce pro tisk
  • do této skupiny lze zařadit i soubor __init__.py, jehož existence označuje složku jako Pythonový modul
  • tisk objektu bez funkce __repr__()
  • tiskne informace o umístění objektu v paměti
class A:
  def __init__(self):
    pass
  
a = A()
print(a)
<__main__.A object at 0x7f147809a8d0>
  • tisk objektu s funkcí __repr__()
  • tiskne řetězec z této funkce
class A:
  def __init__(self):
    pass
  
  def __repr__(self) -> str:
    return "Objekt třídy A."  

a = A()
print(a)
Objekt třídy A.

Kontextové managery

  • konstrukce with
  • vztahují se k veškerému kódu uvnitř bloku (odsazení)
  • vhodné tam kde existuje nějaký logický začátek a konec operace nebo operace, které se s počátkem a koncem pevně pojí
    • např. zápis textu do souboru - soubor otevřeme, zapíšeme text a soubor zavřeme (to nemusí být explicitní, může být i “neviditelný”)
  • objekty, které využívají dvou magických metod __enter__ a __exit__
  • ukázka se souborem
  • ukázka s vlastním managerem

Kontextové managery ukázka

file = open("soubor.txt", "w")
file.write("Text")
file.close()  # na tohle se často zapomene - může/nemusí být problém
  • s kontextovým managerem
with open("soubor.txt", "w") as file:
    file.write("Text")
  • i komplexnější operace, např. připojení k databázi a uzavření spojení

Typování proměnných

  • teoreticky není vůbec nutné
  • zejména u větších knihoven však výrazně pomáhá s managementem kódu
  • zastoupí částečně i dokumentaci, protože signatura funkce nese veškeré nezbytné informace
  • při vytvoření proměnné v kódu můžeme za : uvést typ proměnné
  • obvykle hlavně u funkcí
  • za -> uvádíme návratový typ funkce
import typing


def fun(vals: typing.List[float], round_up: bool = True) -> typing.List[int]: ...

Typování - komplexnější

  • typing.Optional[] - pro proměnné, které mají buď specifickou hodnotu nebo None
  • typing.Union[float, int] - pro proměnné, které mohou mít více typů
  • typing.Any - pro proměnné, které mohou mít libovolný typ (většinou se snažíme vyhnout)
  • typing.Literal[] - pro proměnné, které mohou mít pouze určité hodnoty
    • používá se jako typing.Literal["a", "b"]
  • typing.Callable - pro proměnné, které jsou funkce
    • typing.Callable[[int, int], int] - funkce s dvěma vstupy (celá čísla) a jedním výstupem (celé číslo)

Objekt Path z knihovny pathlib

  • objekty cesty k souborům a složkám
  • umožňuje snadnější manipulaci s cestami
  • přepsané magické metody, umožňují spojovat cesty pomocí operátoru /
    • např. cesta = Path("slozka") / "soubor.txt"
  • umožňuje snadnější práci s cestami, než klasické operace s řetězci
  • tyto objekty lze používat na různých místech v Python knihovnách a funkcí (např. open())
  • pokud potřebujeme cestu v podobě textového řetězce, lze ji získat pomocí metody as_posix()
    • cesta_text = cesta.as_posix()

Delší skripty

  • Python pouští skripty standardně celé
    • problém se čtením u delších souborů
    • při importu se také interpretuje celý soubor (může se spustit kód, který by neměl)
  • typicky řada funkcí nasledovaná kódem, který má běžet
  • část skriptu, která se má spustit uvádět do if __name__ == "__main__":
    • tato část se spustí, pouze pokud je spuštěn soubor přímo, nikoliv importován

Delší skripty - příklad

def fn_a(): ...
def fn_b(): ...
def fn_c(): ...

a = 1 + 1 # kód, který se vykoná vždy (spuštění i import)

if __name__ == "__main__":
    # vykoná se pouze při explicitním spuštění souboru
    fn_a()
    fn_b()
    fn_c() 

Formátování pomocí black a isort

  • isort řadí importy dle určité logiky
  • black formátuje kód dle pravidel, má minimum nastavení a tak je vysoce konstantní napříč projekty
  • zpočátku obvykle nevyhovuje uživateli na 100%, ale jde o zvyk

Kontroly pomocí mypy, pep8, flake8, pylint

  • zvýrazňování možných chyb, příkladů špatné praxe a dalšího v kódu
  • ne vždy musí jít o zásadní problém
  • v moha situacích to předchází chybám
  • např. mypy nutí uživatele psát rozumně typovaný kód a používat ho správně
  • celkově dodržování těchto pravidel vede k použitelnému a čitelnému kódu
  • většina těchto nástrojů má svoje nastavení a možnosti, jak je upravit
  • konkrétní hlášky lze obvykle potlačit, pokud jsou nežádoucí, buď přímo v kódu, nebo v konfiguraci nástroje

Debugování

  • závisí na použitém IDE
  • občas se nahrazuje použitím print(), ale to nemusí být dostačující
  • proces nahlížení na stav programu za jeho běhu
  • umožňuje nahlížet do paměti programu (proměnné a jejich hodnoty) v jeho různých fázích a stavech
  • breakpointy - zastavení programu v určitém místě
  • v Pythonu máme možnost do proměnných za běhu i zasahovat
  • ukázka v VS Code

Dotazy?