Lekce 24 - Výčtové typy (enum) a konstanty v Pythonu Nové
V minulé lekci, Abstraktní třídy v Pythonu, jsme si vysvětlili, co jsou to abstraktní třídy.
V tomto tutoriálu OOP v Pythonu se podíváme na výčtové typy a konstanty.
Výčtové typy a konstanty v Pythonu
V programech se nám často stává, že nějaká proměnná může nabývat několika přednastavených hodnot. Příkladem může být stav objednávky v internetovém obchodě. Objednávka může být nová, přijatá, potvrzená nebo dokončená. Žádného jiného stavu nabývat nemůže. Podobným příkladem jsou např. dny v týdnu, pracovní pozice a podobně.
S dosavadními znalostmi bychom si stav objednávky ukládali asi jako
textový řetězec. To však může být častým zdrojem chyb. Nemáme žádný
mechanismus, který nám zkontroluje, že je hodnota zadaná správně. Proto
Python obsahuje tzv. výčtové typy z modulu
enum
, ve kterém najdeme třídu Enum
.
Výčtové typy (Enum)
Výčtové typy byly v Pythonu zavedeny od verze 3.4. Jejich definice je přímočará:
from enum import Enum class StavObjednavky(Enum): NOVA = 1 PRIJATA = 2 POTVRZENA = 3 DOKONCENA = 4
Definovali jsme si třídu StavObjednavky
, která dědí ze
třídy Enum
, kterou jsme si importovali. Každý člen tohoto
výčtu (NOVA
, PRIJATA
…) má dvě části:
- jméno (např.
NOVA
) - a hodnotu (např.
1
).
Hodnota může být libovolná neměnná konstanta. V našem případě jde o číslo, ale můžeme použít i textový řetězec nebo jiný datový typ. Čísla zde slouží jen jako interní reprezentace, například pro ukládání do databáze nebo pro snadné porovnávání.
IDE nám stavy nabízí v našeptávači:

Použití je následující:
stav = StavObjednavky.PRIJATA if stav == StavObjednavky.PRIJATA: print("Objednávka byla přijata.")
Díky výčtovému typu máme k dispozici jasně
pojmenované hodnoty. Pokud proměnnou anotujeme typem
StavObjednavky
, nástroje pro typovou kontrolu pohlídají, že
obsahuje jen členy tohoto výčtu.
Hodnotu člena získáme přes atribut value
:
print(StavObjednavky.DOKONCENA.value) # vypíše: 4
Enum jako třída s logikou
Výčtové typy v Pythonu se chovají jako běžné třídy, můžeme tedy každému členovi přidat vlastní data (například popisný text) a i vlastní metody. To je užitečné, pokud chceme, aby samotný výčtový typ nesl nejen název a hodnotu, ale i další logiku, která s ním souvisí.
V tomto příkladu má každý stav objednávky přiřazený popisný text a
metoda __str__()
zajistí, že se při vypsání člena
Enum
zobrazí právě tento text:
from enum import Enum class StavObjednavky(Enum): NOVA = "Nová objednávka" PRIJATA = "Objednávka byla přijata ke zpracování" POTVRZENA = "Objednávka byla potvrzena" DOKONCENA = "Zboží bylo expedováno" def __str__(self): return self.value stav = StavObjednavky.NOVA print(stav) # vypíše: Nová objednávka
Každý člen Enum
je ve skutečnosti instance této
třídy a může mít vlastní data i metody. V našem případě jsme
přímo při deklaraci nastavili textový popis.
Více hodnot najednou
Někdy potřebujeme, aby proměnná obsahovala více hodnot současně. Typickým příkladem jsou uživatelská práva – uživatel může mít právo na čtení i zápis zároveň.
V Pythonu existují dva hlavní způsoby, jak to řešit.
Množina členů Enum
Jednoduché řešení je použít set
a vložit do něj členy
výčtu. Nejprve si definujeme práva jako Enum
:
from enum import Enum, auto class Prava(Enum): ZADNA = auto() ZAPIS = auto() CTENI = auto() SPUSTENI = auto() VYMAZANI = auto() TISK = auto()
Funkce auto()
z modulu enum
členům přiřadí
postupná celá čísla od 1
. Díky tomu nemusíme
ručně číslovat a když později přidáme nové právo doprostřed, nic
nepřečíslováváme.
Poté deklarujeme množinu práv, do níž přiřadíme například právo pro čtení a tisk:
prava_uzivatele = {Prava.CTENI, Prava.TISK}
Pokud bychom chtěli nějaká práva přidat, použijeme:
prava_uzivatele.add(Prava.SPUSTENI)
Můžeme také práva odebrat:
prava_uzivatele.discard(Prava.CTENI)
Práva uživatele zkontrolujeme takto:
if Prava.TISK in prava_uzivatele: print("Mohu tisknout!")
A všechna práva vypíšeme:
print(prava_uzivatele) # vypíše: {<Prava.SPUSTENI: 4>, <Prava.TISK: 6>}
Bitové příznaky (IntFlag
)
Pro efektivní práci s kombinacemi práv můžeme z modulu enum
použít třídu IntFlag
. Členy pak lze kombinovat
bitovými operátory:
from enum import IntFlag, auto class Prava(IntFlag): ZADNA = 0 ZAPIS = auto() CTENI = auto() SPUSTENI = auto() VYMAZANI = auto() TISK = auto()
Funkce auto()
při použití s třídou IntFlag
přiřazuje hodnoty jako mocniny dvojky (1
, 2
,
4
, 8
…), takže se dají bezpečně kombinovat
pomocí bitových operací.
Nyní můžeme nastavit práva pro čtení a zápis tímto způsobem:
prava_uzivatele = Prava.CTENI | Prava.ZAPIS
Použili jsme bitový operátor OR (|
), uživatel tedy může
číst i zapisovat.
Přidání dalšího práva vypadá takto:
prava_uzivatele |= Prava.SPUSTENI
Ukažme si také odebrání práv:
prava_uzivatele &= ~Prava.CTENI
Můžeme si opět udělat kontrolu:
if prava_uzivatele & Prava.TISK: print("Mohu tisknout!") else: print("Nemám právo tisknout!")
Tento způsob nastavování práv využijeme, pokud potřebujeme ukládat práva jako jedinou číselnou hodnotu (bitovou masku), posílat je po síti nebo ukládat do databáze.
V příkladech jsme použili několik speciálních
zápisů. Operátor |=
přidá nové právo k těm
stávajícím, zatímco &= ~
konkrétní právo odebere.
Pomocí operátoru &
pak v podmínce ověřujeme, zda uživatel
dané právo má. Tyto operace fungují proto, že každé právo má hodnotu s
jediným nastaveným bitem (mocniny dvou).
Konstanty
Kromě výčtových typů se v kódu můžeme setkat i s konstantami, tedy proměnnými, jejichž hodnota by měla zůstat stejná po celou dobu běhu programu. Python nemá speciální klíčové slovo pro jejich definici, ale podle běžné konvence se zapisují VELKÝMI_PÍSMENY a neočekává se, že se budou měnit:
MINIMALNI_DELKA_HESLA = 5
Tím říkáme, že proměnná MINIMALNI_DELKA_HESLA
by se
neměla měnit. Pokud chceme, aby na možné přepsání upozornily nástroje
pro kontrolu typů, můžeme použít anotaci Final
z modulu
typing
:
from typing import Final MINIMALNI_DELKA_HESLA: Final[int] = 5
Pokud nyní někdo takto deklarovanou hodnotu přepíše, nástroje pro statickou analýzu (např. SonarQube) na to upozorní. Zvládne to i naše vývojové prostředí PyCharm:

Statická analýza je součást statického testování. Při něm nástroje kontrolují zdrojový kód bez jeho spuštění. Dokážou tak odhalit potenciální chyby, porušení konvencí nebo nechtěné změny konstant ještě před spuštěním programu. Více se o statickém testování dozvíte v lekci Statické testování a v kurzu ISTQB.
Konstanty na třídách
Konstanty se často definují i uvnitř tříd, pokud s nimi třída úzce souvisí:
from typing import Final class Uzivatel: MINIMALNI_DELKA_HESLA: Final[int] = 5 print(Uzivatel.MINIMALNI_DELKA_HESLA) # vypíše: 5
Tímto způsobem je hodnota "uzamčena" uvnitř třídy a dává smysl jen v jejím kontextu.
Python přepsání konstant technicky
nezakazuje. Final
je jen upozornění pro
vývojáře, ne skutečný zámek při běhu programu. Editor a kontrola kódu
nás varují, ale samotný Python změně nezabrání.
V příští lekci, Nejčastější chyby Python nováčků - Umíš pojmenovat objekty?, si ukážeme nejčastější chyby začátečníků v Pythonu ohledně pojmenování tříd, metod a atributů.