Lekce 16 - Typový systém a type hints v Pythonu
Přestože je Python dynamicky typovaný, stále se jedná o silně typovaný jazyk, který vyžaduje přiřazení typu proměnné. Toto se sice většinou odehrává automaticky, ale i tak jsou případy, kdy se hodí si typy v Pythonu manuálně a explicitně uvést. Například díky uvedení typů může naše IDE lépe kontrolovat správnost našeho kódu. Pro větší projekty je tato praktika velmi užitečná.
Samotný Python námi uvedené typy ignoruje a přiřazuje si
je vždy sám, nehledě na to, co mu my explicitně uvedeme. Toto uvedení tedy
doopravdy slouží pouze jako informace pro nás, popř. pro IDE nebo nástroj mypy
, který typy
ověřuje.
Základní typy v Pythonu a jejich použití
Základní typy v Pythonu jsou:
int
pro celé číslofloat
pro reálné číslobool
pro hodnotyTrue
/False
str
pro řetězceNone
proNone
Pokud chceme explicitně deklarovat typ proměnné, použijeme k tomu
dvojtečkový operátor :
:
text: str = "Ahoj světe!"
Nyní je jasně řečeno, že text
je typu str
. Zde
by to bylo stejně poznat podle hodnoty "Ahoj světe!"
, takže je
tu takové označení trochu zbytečné. V programu je ale spoustu míst, kde to
již tak jasné není. Např. můžeme typovat i parametry funkcí a jejich
návratové hodnoty:
def generuj_ahoj(jmeno: str) -> str: return "Ahoj, " + jmeno + "!"
Již zmíněnými nástroji je v tuto chvíli pak možné zkontrolovat, zda nám vše sedí, případně vygenerovat k funkcím dokumentaci včetně typů.
Další typy v Pythonu
Samozřejmě toto nejsou všechny typy, které se mohou v Pythonu objevit.
Jistě sami zvládnete vyjmenovat mnohé další - list
,
dict
atd. Že je proměnná typu list
můžeme
označit pomocí:
muj_list: list = [1, 2, 3]
Toto by nám ale nebylo moc užitečné. Sice bychom věděli, že daná
proměnná je list
, ale nevěděli bychom jakého typu jsou prvky,
které se v tomto listu nacházejí.
Knihovna typing
Přesně s tímto nám pomůže knihovna
typing
:
from typing import List, Set, Dict, Optional, Callable
Typování seznamů a množin
První dvě importované položky nám přijdou jistě intuitivní, typ prvků v nich uložených se určuje pomocí hranatých závorek:
muj_list: List[int] = [1, 2, 3] muj_set: Set[int] = set(muj_list)
Slovníky
Pokud chceme typovat slovník, s jedním takovým parametrem si nevystačíme. Budeme tedy potřebovat parametry dva - první pro typ klíče a druhý pro typ hodnot:
muj_slovnik: Dict[int, str] = {1: 'jedna', 2: 'dva', 3: 'tri'}
Samozřejmě můžeme jako parametry používat i složitější typy, než jen a pouze ty primitivní:
muj_slovnik2: Dict[int, List[str]] = {1: ['jedna', 'one'], 2: ['dva', 'two'], 3: ['tri', 'three']}
Optional
Kdy se nám ale může hodit typ Optional
? Optional
ve zkratce značí to, že daná proměnná může nabývat hodnot typu prvního
parametru nebo None
. To se nám hodí například, pokud bychom
chtěli vytvořit funkci na dělení a chtěli zabránit dělení nulou:
def deleni(a: float, b: float) -> Optional[float]: if b == 0: return None # nesmíme dělit nulou return a / b
Callable
Poslední typ, který jsme si importovali, se jmenuje Callable
.
Jak již název napovídá, jedná se o typ něčeho, co se může volat, tedy
funkcí:
def pridej_jedna(a: int) -> int: return a + 1 operace: Callable = pridej_jedna print(operace(5)) # vypíše 6
Vytváření vlastních typů
Typy v Pythonu můžeme vytvářet třemi způsoby:
- typové aliasy
- třídy
- pojmenovaný slovník
Typové aliasy
Pokud nějaký komplikovaný typ používáme často, můžeme si vytvořit typový alias, který se vytvoří jednoduchých přiřazením původního typu do proměnné. Například se nám to může hodit při definici matic:
matice: List[List[int]] = [[1, 2], [3, 4]]
můžeme díky vytvoření typovému aliasu napsat jako
Matrix = List[List[int]] matice: Matrix = [[1, 2], [3, 4]]
Třídy
Každá třída se bere také jako vlastní typ, ať už ji importujeme, nebo sami vytvoříme:
class MojeTrida: pass moje_promenna: MojeTrida = MojeTrida() from queue import Queue fronta: Queue = Queue()
Problémem, na který bychom mohli narazit, by bylo, že bychom chtěli využít typ, který je deklarován až dále v souboru (popř. typ dané třídy samotné). V době čtení daného řádku jej tedy parser nezná. Tomu můžeme předejít tak, že místo přímého odkazu na danou třídu uzavřeme její jméno do uvozovek jako textový řetězec. Např.:
from typing import Optinal, Any class LinkedList: def __init__(self): self.value: Optional[Any] = Noneprvky self.next: Optional["LinkedList"] = None
Typovaný slovník
Posledním typem vytváření typů v Pythonu, který dnes probereme, je
vytvoření typovaného slovníku (TypedDict
), který se poprvé
objevil v Pythonu 3.8. Jedná se o jakousi abstrakci nad klasickým slovníkem,
kdy ale explicitně uvedeme, které klíče s jakým typem hodnot se mohou ve
slovníku nacházet:
from typing import TypedDict class AdresaDomu(TypedDict): mesto: str ulice: str cislo_domu: int AdresaDomu = TypedDict('AdresaDomu', mesto=str, ulice=str, cislo_domu=int) AdresaDomu = TypedDict('AdresaDomu', {'mesto': str, 'ulice': str, 'cislo_domu': int}) moje_adresa: AdresaDomu = {'mesto': 'Stare Mesto', 'ulice': 'Nova ulice', 'cislo_domu': 52}
Alternativní systém typování
Tento systém, který jsme si ukázali, je v Pythonu dostupný od verze 3.5,
takže pokud používáte Python 3.4 a starší, nemáme dostupné nic z
knihovny typing
, ani dvojtečkový operátor. Přesto ale můžeme
typovat - a to pomocí komentářů. Takže například řetězcovou proměnnou
bychom deklarovali následovně:
muj_text = "Ahoj" # type: str
Tento systém je možné využívat i v Pythonu 3.5 a novějším, přičemž v některých konkrétních situacích je to stále jediný možný způsob typování bez toho, aby nám interpreter hlásil část kódu, kterou neumí zparsovat.
Další studium
V této lekci jsme si ukázali, jak v Pythonu používat základní typování, což nám vystačí pro naprostou většinu běžných případů. Několik posledních verzí Pythonu prokázalo, že typování je věc, kterou se chce jazyk stále více zabývat a je proto stále ve vývinu. Pokud bychom chtěli najít nejaktuálnější postupy při typování, nejlepší bude konzultovat oficiální dokumentaci.
V následujícím cvičení, Řešené úlohy k 12.-15. lekci OOP v Pythonu, si procvičíme nabyté zkušenosti z předchozích lekcí.
Komentáře
Zatím nikdo nevložil komentář - buď první!