Python je dynamicky typovaný jazyk, což znamená, že o žádném místě v paměti (referencovaném nějakým jménem z programu) se dopředu neví, co v něm je, dokud se tam interpretr nepodívá. Což je sice pohodlné pro rychlou práci, nicméně to nebývá – alespoň v případě hlavních interpretrů Python'u – příliš rychlé.
Nejen z tohoto důvodu si tak postupem času spousta lidí začala do pythoních programů dopisovat označení typů a k nim armádu nástrojů na jejich ověřování či rovnou vynucování. Zkrátka typy..
A tak časem vývojáři Python'u vzali uvedený stav na vědomí a pro Python 3.0 v PEPu 3107 ustavili jeden podporovaný způsob zápisu označení nejen typů u funkcí, tzv. anotace funkcí (function annotations). A ještě později, pro Python 3.5, v PEPu 484 doplnili i konkrétní způsob zápisu typů, včetně složených za pomoci modulu typing.
Anotovat je u funkcí možno dvě věci:
Parametry, a to jak poziční, tak pojmenované (s výchozí hodnotou):
Parametry se tedy anotují pomocí dvojtečky :
, za níž následuje výraz, jehož použití a vyhodnocení je zcela mimo pythoní interpretr – co si s ním která externí knihovna, nástroj či uživatel provedou, je zcela na nich. Výrazem tudíž může být prakticky naprosto cokoliv (tedy samozřejmě pokud je to validní pythoní výraz).
Návratovou hodnotu funkce:
Návratová hodnota funkce se tedy anotuje pomocí sekvence znaků ->
, za níž následuje validní pythoní výraz, jehož interpretace je opět – stejně jako u anotací parametrů – zcela v rukou externího nástroje.
Kdyby bylo možné anotace u funkcí pouze zapsat, ale nedalo se k nim nijak z Python'u dostat, nebylo by to příliš platné. Sice byste mohli na vaše pythoní kódy pouštět třebas nějaké externí analyzátory, ale nebyla by to moc zábava.
Součástí standardu však je i způsob přístupu k těmto anotacím – pomocí magického atributu __annotations__ příslušné funkce:
Anotace jsou tedy dostupné ve slovníku pod jménem odpovídajících parametrů. Přitom případná návratová hodnota funkce je dostupná pod předdefinovaným jménem return (to totiž jako jméno parametru nejde pochopitelně použít, takže nemůže dojít ke kolizi názvů).
PS: Možnost spravovat anotace z vlastního pythoního kódu nabízí docela nečekané možnosti, jak si za chvíli ukážeme. Nejdříve však ještě pár příkladů zápisu anotací.
První typické použití, které asi napadne každého, bude označení typů parametrů a návratové hodnoty funkce, jak je zvykem (především) u staticky typovaných jazyků:
Nicméně je třeba opět zdůraznit: Žádný pythoní interpretr nemá za povinnost tyto anotace nějak zpracovávat! Jejich jediným úkolem je příslušné anotace poskytnout jako vlastnosti funkce pro další použití, ale sami je (aspoň u většiny) nijak nepoužívají a dále nezpracovávají.
Anotací však může být libovolný pythoní výraz, kupříkladu tedy třebas obyčejný řetězec:
Třeba je externí knihovna daného projektu použije pro tvorbu vylepšené dokumentace, kdo ví?
A asi je vidět, že se oboje dá snadno spojit dohromady – stačí použít všeobjímající n-tici:
Pokud si externí program, který anotace zpracovává, umí s uvedeným zápisem poradit, je vše v pořádku.
Důvodem, proč jsem tuto přednášku vůbec napsal, je sada úžasných dekorátorů pro využití anotací od Roddyho MacSweenyho (zde hodně upravené):
Vidíme, že dekorátor @checkargs
pomocí anotací a introspekce (funkce isinstance()) ověřuje, zda zadané parametry skutečně splňují předepsaný typ, zatímco dekorátor @coerceargs
se dokonce snaží parametry na daný typ převést (přímou aplikací zadaného typového konstruktoru na zadaný parametr).
Signature
.
Nicméně je třeba říci, že použitím kontroly typů přijdeme o klasické pythoní duck typing, tedy zjednodušeně možnost zpracování různých typů, které však implementují stejné metody (zde možnost iterace po prvcích):
Je třeba říci, že pokud budete anotace používat, nakonec se neobejdete bez detailní znalosti modulu typing, který mimo jiné umožňuje vůbec anotovat složené typy, protože to pochopitelně se základními konstruktory přítomnými (nejen) v nulovém jmenném prostoru nejde.
Zároveň je třeba pamatovat na to, že Python byl/je/zůstává dynamicky typovaným jazykem a žádný z jeho interpretrů nemá v popisu práce s anotacemi dělat cokoliv jiného, než je „cpát“ do atributu __annotations__. Anotace v Python'u jsou a dlouho (navěky?) budou pouze pro potřeby externích nástrojů (či editorů), které nemusí vůbec provádět typovou kontrolu, ale klidně něco úplně jiného (například budovat lepší nápovědu).