Pouze tento týden sleva až 80 % na e-learning týkající se Javy
Nauč se s námi víc. Využij 50% zdarma na e-learningové kurzy.
discount week 50

Lekce 16 - Typový systém a type hints v Pythonu

V minulé lekci, Magické metody v Pythonu - Kolekce a deskriptory, jsme se podívali na magické metody kolekcí, metody pro řízení atributů a na deskriptory 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é číslo
  • float pro reálné číslo
  • bool pro hodnoty True/False
  • str pro řetězce
  • None pro None

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

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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:

  1. typové aliasy
  2. třídy
  3. 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ým 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 Optional, 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í.


 

Předchozí článek
Magické metody v Pythonu - Kolekce a deskriptory
Všechny články v sekci
Objektově orientované programování v Pythonu
Článek pro vás napsal Adam Hlaváček
Avatar
Jak se ti líbí článek?
1 hlasů
vývoji užitečných aplikací zjednodušujících každodenní život
Aktivity

 

 

Komentáře

Avatar
Antonín Martykán:14. května 14:07

Nechápu jádro problému: k čemu je typování, když do "otypované" proměnné mohu uložit jiný datový typ?

 
Odpovědět
14. května 14:07
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Antonín Martykán
DarkCoder:14. května 20:24

To že má proměnná nějaký typ je nesmírně důležité. Podívejme se na následující inicializace (jazyk C):

char c1 = 65;
signed char c2 = 65;
unsigned char c3 = 65;
short int c4 = 65;
unsigned int c5 = 65;
long long int c6 = 65;

Všechny proměnné v sobě uchovávají hodnotu znaku 'A' (ASCII 65). První tři proměnné c1-c3 zabírají v paměti 1byte, c4 2 byty, c5 4 byty, c6 8 bytů ve 32 bitovém prostředí.

Říká tedy, kolik místa v paměti zabírá proměnná a pomáhá alokovat potřebnou paměť. Přístup k proměnné která zabírá v paměti 1 byte bude také rychlejší než přístup k proměnné typu long long int, která zabírá v paměti 8 bytů.

Dále specifikátory typu upřesňují rozsah hodnot jakých může proměnná nabývat.

Můžou znakové proměnné c1-c3 v sobě držet zápornou hodnotu?

U c1 záleží na implementaci, c2 ano, ale rozsah kladných hodnot je poloviční, c3 nikoli.

Dále to umožňuje udržovat vzájemnou vazbu mezi proměnnými a funkcemi. Pomáhá překladači najít chybu a upozornit na to programátora. Rovněž programátor si nemusí pomatovat co do proměnné uložil a ztrácet čas dohledáváním typu obsahu.

Důvodů, proč typy, je opravdu dost..

Odpovědět
14. května 20:24
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na DarkCoder
Antonín Martykán:14. května 20:38

Asi jsme si nerozuměli :) rozumím tomu, že existují Tebou vyjmenované důvody (za výpis moc děkuji!) Ale přece není možné do proměnné char c1 uložit číslo 300. Nevím jak to je v C pořešené, ale taková proměnná by se neměla inicializovat. Zatímco v Pythonu si mohu “otypovat” proměnnou jako float a pak ji bez problému přepsat na Bool hodnoty, string, int... Jinými slovy: k čemu je typování v Pythonu, když ho překladač “nekontroluje”?

 
Odpovědět
14. května 20:38
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Antonín Martykán
DarkCoder:14. května 21:10

Přiřadit hodnotu 300 do znakové (8-bitové) proměnné nepochybně správné není, neboť z důvodu přetečení rozsahu bude proměnná obsahovat nesmyslnou hodnotu. Je na programátorovi, aby posoudil že to co dělá je správné. Důvodem je zachování rychlosti.

V C se typ proměnné nemění, jde však proměnnou dočasně přetypovat při přiřazení nebo použití. Dále v C lze deklarovat proměnnou jako obecný ukazatel, která může ukazovat na libovolný typ. Bez přetypování však nelze tento ukazatel dereferencovat. Lze použít uniony k uložení libovolného typu který se předem stanoví. Pak může znaková proměnná obsahovat klidně i 8 bytů.

Pokud lze v Pythonu měnit typy proměnné z jednoho na druhý, mě osobně to nepřijde šťastné. Obtížně se kontroluje co aktuálně v proměnné je, dále celá operace je časově náročná (Alokace, převodní funkce, přesun na novou pozici, dealokace). Pokud by se cokoli kontrolovalo, ušetřilo by to jistě programátorovi práci, ale oproti tomu by bylo vše pomalé.

Nejsem Pythonista, osobně bych tento způsob programování nepoužíval. Ale každého věc..

Odpovědět
14. května 21:10
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.