Funkce a třídy

Programování v GIS 2

Jan Caha

2025-03-24

Programovací paradigmata

  • většina jazyků nestojí striktně na jednom paradigmatu, ale využívá jich vícero

  • imperativní (kód popisuje, co se bude dít)

    • procedurální programování (procedury, které ze vzájemně volají)
    • objektové programování (objekty - data a operace s nimi asociované)
    • většina jazyků - např. Python, R, C++
  • deklarativní (kód popisuje požadovaný výsledek)

    • funkcionální, logické a reaktivní
    • např. jazyk SQL, ale i funkcionální přístupy v Pythonu a R

Python

  • pro běžné použití kombinujeme obvykle procedurální a objektový přístup
  • procedurální přístup - funkce, které mají parametry
  • objektový přístup - objekty, které mají metody (funkce asociované s objektem) a atributy (proměnné objektu)
  • není problém s kombinací, viz cvičení (GDAL/OGR má objekty, my si k nim píšeme funkce)

Funkce

  • dělí kód na dílčí logické bloky, které mohou být znovu použitelné
  • měly by dělat jednu věc a pouze jednu věc
  • délka by neměla být velká (aby bylo možné tělo funkce logicky přečíst a pochopit)
  • snaha navrhovat funkce spíše obecně než specificky

Funkce příklad

def fn(a: str, b: int, c: float = 0.5) -> float: ...
  • funkce se třemi proměnnými
    • první dvě povinné
    • třetí nepovinná s výchozí hodnotou

Pozor

Proměnné s výchozí hodnotou musí být až za proměnnými bez výchozí hodnoty. Nelze mít proměnnou bez výchozí hodnoty za proměnnou s výchozí hodnotou funkce by nebyla validní a Python by vykazoval chybu při snaze takovou funkce načíst.

  • funkce vrací float
  • mohou existovat funkce bez návratové hodnoty None
  • nezapomínat na typování parametrů a návratové hodnoty

Funkce obecná specifická

# specifické
def vynasob_dvema(a: float) -> float:
    return a * 2


# obecné
def vynasob(a: float, b: float) -> float:
    return a * b

Tvorba upravené funkce (předefinování parametrů)

  • funkce partial z modulu functools
  • vytvoří novou funkci, která má některé parametry přednastavené a uloží ji do proměnné
from functools import partial

# zadáme funkci a potom parametr(y) které chceme předdefinovat
multiply_hodnotu_dvema = partial(vynasob, b=2)
hodnota = multiply_hodnotu_dvema(3)

Objekty

  • třída je šablona objektu, jednotlivý objekt je pak instancí třídy
  • objekt je prvek, které má vlastní informace a s kterými můžeme vykonávat operace
  • metody, vlastnosti (property) a atributy
  • složitost se může různit

Objekty detaily

  • první parametr metody je vždy self (označení instance objektu)
  • atributy i metody mohou být veřejné nebo soukromé (neměly by být přímo dostupné zvenčí třídy) – v Pythonu se to řeší konvencí, soukromé atributy a metody začínají podtržítkem (např. _atribut), tyto metody a atributy nebudou nápovědy v IDE navrhovat a programátor by se měl ideálně jejich použití vyhnout
  • magické metody objektů (např. __init__, __str__, __add__, __eq__ a další) a jejich použití
  • vlastnost (property), mohou být buď statické (přímo hodnota objektu) nebo dynamické (metoda s dekorátorem @property)

Metody objektu

  • metody objektu jsou funkce, které jsou součástí objektu, první parametr je vždy self
  • metody mohou být i tzv. statické metody, které nemají přístup k atributům objektu, ale jsou součástí třídy , označují se dekorátorem @staticmethod

Objekt - příklad

class Zamestnanec:
    def __init__(self, jmeno: str, hodinovy_plat: float):
        self.jmeno = jmeno
        self._hodinovy_plat = hodinovy_plat
        self.uvazek = 1.0

    def plat(self) -> float:
        return self._hodinovy_plat

    def nastav_plat(self, hodinovy_plat: float) -> None:
        self._hodinovy_plat = hodinovy_plat

    def zvys_plat(self, navyseni_o: float) -> None:
        self._hodinovy_plat += navyseni_o

    def mesicni_plat(self, odpracovanych_hodin: float) -> float:
        return self._hodinovy_plat * odpracovanych_hodin

    ## statická metoda, jedná se o ukázku, ale zde by mohla být i normální metoda
    @staticmethod
    def delka_pracovniho_dne(uvazek: float = 1.0) -> float:
        return 8 * uvazek

    @property
    def hodin_denne(self) -> float:
        return self.delka_pracovniho_dne(self.uvazek)

    @property
    def denni_plat(self) -> float:
        return self._hodinovy_plat * self.hodin_denne

    def __repr__(self) -> str:
        return f"Zamestnanec {self.jmeno} s platem {self._hodinovy_plat} za hodinu"

Použití objektu

odpracovane_hodiny = 160
zam = Zamestnanec("Testovaci Zamestnanec", 250)
print(f"Denní plat: {zam.denni_plat}")
zam.uvazek = 0.5
print(f"Denní plat: {zam.denni_plat}")
print(f"Plat: {zam.mesicni_plat(odpracovane_hodiny)}")
zam.zvys_plat(50)
print(f"Plat po zvýšení: {zam.mesicni_plat(odpracovane_hodiny)}")
Denní plat: 2000.0
Denní plat: 1000.0
Plat: 40000
Plat po zvýšení: 48000

Dědičnost

  • třída může dědit od jiné třídy - přebírá její atributy a metody
  • lze přidat nové atributy a metody nebo přepsat existující
  • v praxi se s tím setkáváme běžně, např. GeoDataFrame dědí z DataFrame (přidává sloupec geometrie a prostorové metody)
  • syntaxe: class Potomek(Rodic):
  • funkce super() umožňuje volat metody rodičovské třídy

Dědičnost - příklad

class Manazer(Zamestnanec):
    def __init__(self, jmeno: str, hodinovy_plat: float, pocet_podrizenych: int):
        # volání konstruktoru rodičovské třídy
        super().__init__(jmeno, hodinovy_plat)
        self.pocet_podrizenych = pocet_podrizenych

    def bonus(self) -> float:
        """Bonus za vedení týmu."""
        return self.pocet_podrizenych * 500

    def mesicni_plat(self, odpracovanych_hodin: float) -> float:
        """Přepsaná metoda - měsíční plat včetně bonusu."""
        return super().mesicni_plat(odpracovanych_hodin) + self.bonus()

    def __repr__(self) -> str:
        return f"Manažer {self.jmeno}, tým: {self.pocet_podrizenych}"

Dědičnost - použití

mng = Manazer("Vedoucí Projektu", 350, 5)
print(mng)
print(f"Měsíční plat: {mng.mesicni_plat(160)}")
# Manazer je zároveň i Zamestnanec
print(f"Je zaměstnanec: {isinstance(mng, Zamestnanec)}")
Manažer Vedoucí Projektu, tým: 5
Měsíční plat: 58500
Je zaměstnanec: True

Docstringy

  • dokumentační řetězce uvnitř funkcí, tříd a modulů
  • umístěny hned za definicí funkce/třídy jako první řádek
  • slouží jako dokumentace, kterou lze programově číst (např. help(funkce))
  • IDE je zobrazuje jako nápovědu při najetí myší
  • extenze autoDocstring ve VS Code generuje šablonu

Dotazy?