A nebylo by jednodussi misto vraceni None vyhodit vyjimku? Pak by si mypy nestezoval a nekorektni vstupy by byly osetrene... Samozrejme zalezi na tom, jak casto se ocekava, ze bude funkce s nekorektnimi parametry volana...
Nebo mypy hlida i vyhazovane vyjimky? Ostatne, neco jako javovske checked/unchecked exceptions by se hodilo nejen v pythonu, ale i C++ a dalsich jazycich...
2. 6. 2020, 09:18 editováno autorem komentáře
... protože jeden z důvodů velké obliby Pythonu je právě možnost elegantního zápisu programů bez toho, aby se musel vývojář soustředit na (v daném kontextu) nepodstatné detaily.
Stranou obecná poznámka ohledně dynamicky typovaných jazyků. Tohle mě nepřestává fascinovat, na jedné straně je to nepodstatný detail, ale potom vývojář musí vytvořit milion unit testů, aby pokryl triviální v jiných jazycích běžnou typovou kontrolu nebo páchat podobné šílenosti jako níže:
assert isinstance(n, int), "Integer expected"
Místo jednoho typu u parametru máme celý řádek v kódu kontrolující správný typ, navíc je to runtime check, takže podpora statické analýzy validity kódu bude diskustabilní. Přitom tohle je důležité i z hlediska čitelnosti a dokumentace, abych hned viděl, co za vstup můžu do funkce nacpat...
Vtipné je, že všechny dynamicky typované jazyky už to pochopily a nějakým způsobem už typy zavedly či zavádějí. Bohužel za tu daň, že je spousta existujícího kódu, který ty typové informace postrádá a u některých jazyků vzniklo časem asi 50 transpilerů, které se snažily problém vyřešit (ano, je to ten oblíbený JavaScript). Python je ještě značně pozadu, co se týče deklarací, aby byl jasný scope proměnné, dokonce i v JavaScript před nějakou dobou zavedli let a const a žádný soudný člověk staré var nepoužívá...
Nicméně, provedl jsem krátký test a přemýšlím, nakolik je ta kontrola vyžadována v runtime a nakolik je jenom pro analýzu kódu. Tohle normálně projde a spadne až později na násobení, ačkoliv u parametru je striktně int
místo Optional[int]
:
#!/usr/bin/env python3
from typing import Optional
def x(a: int) -> Optional[int]:
return a*2
print(x(None))
co znamena "spadne"? Az pri spusteni toho kodu?
me mypy ukazuje chybu
"Argument 1 to "x" has incompatible type "None"; expected "int""
ta funkce je korektni, Optional[int] je podtyp int, ale volate to se spatnym typem parametru. mypy na to upozorni aniz byste to musel spustit.
2. 6. 2020, 19:34 editováno autorem komentáře
Tím jsem myslel, když ten program pustím, mypy to pozná. Odpověď je, jak jsem přečetl, že tahle typová kontrola je pouze pro statickou analýzu, v runtime se tahle informace vůbec nepoužívá. Takže "správné" řešení v pythonu je stále mít obojí - typy plus assert instanceof...
Ohledně druhého příspěvku - jasně, že kontroly na validní hodnoty argumentů jsou v některých případech stále potřeba. Ale typicky to bude malá až prázdná podmnožina toho, co musím udělat v dynamicky typovaných jazycích. O zbytek se postará kompilátor. Ta absurdita je v tom, že programátoři tvrdí, jak absence typů zjednodušuje kód a potom ji řeší ručně přes instanceof a napíšou dvakrát tolik kódu, co ve staticky typovaných jazycích, a k tomu ještě další dávku v unit tests...
pokud nepisete funkce urcene pro interaktivni pouziti, tak redundantni runtime overovani typu psat nemusite, kod, ktery neprojde kontrolou mypy muzete povazovat za nevalidni. Ve statickych jazycich take nemate runtime typove testy, co neprojde kompilaci, to nespustite.
anotace jdou cist v runtime, existuji knihovny, ktere podle nich overuji argumenty funkci, ale nejde to uplne primocare, v runtime chcete overovat jine vlastnosti, nektere vlastnosti overovat v runtime nejde, nechcete overovat, ze vsechny polozky listu obsahuji int.
2. 6. 2020, 23:06 editováno autorem komentáře
" ale potom vývojář musí vytvořit milion unit testů, aby pokryl triviální v jiných jazycích běžnou typovou kontrolu nebo páchat podobné šílenosti jako níže:"
ten priklad faktorialu v clanku pekne ukazuje omezeni statickych typovych systemu, nelze omezit typ parametru na kladna cisla. Bez kontrol v case behu a testu se neobejdete.
Ono je to vždycky něco za něco. Aktivně používám víc jazyků, včetně dynamicky typovaného Pythonu, ovšem samozřejmě v Pythonu si za dynamické typování draze zaplatíme - jak časem běhu, tak i - jak uvádíte - tím, že prostě některé potenciální chyby buď ignorujeme nebo nastanou :/
Mypy je (samozřejmě IMHO) velmi vhodné pro ty funkce a metody, které tvoří veřejné API. Tam je potom typová deklarace součástí rozhraní a pomáhá to oběma stranám - jak autorovi API, tak i jeho konzumentovi.
Ale opět - ten typový systém samozřejmě není dokonalý, a popravdě nevím o žádném, který by vždy vyhovoval a byl řešitelný v rámci statických kontrol (tedy před runtime). Stačí si představit funkci, která akceptuje seznam uživatelů, přičemž uživatel je nějaká struktura, která může mít další zanořené atributy - jak to vůbec typově dobře popsat a zkontrolovat?
U assertů pozor na to, že se dají vypnout (python -O, PYTHONOPTIMIZE), takže kódem může nakonec proplout úplně jiná výjimka, než AssertionError.
Při čistém stylu programování v Pythonu se "assert" používá jen pro deklaraci podmínek, jejichž pravdivost vyplývá z logické správnosti kódu, nikoli ke kontrole vstupů. V takovém případě AssertionError znamená: "V tomto programu je pravděpodobně něco špatně." To je pak důležitější než, že se to týká chybného typu. Je-li program řádně otestován, už by se AssertionError neměla objevit a proto lze kontrolu vypnout.
To ovšem platí jen za jistých, byť běžně očekávaných podmínek. Python totiž například umožňuje dědičnost skoro u všech typů kromě None, bool (a částečně kromě typing.Final), takže lze udělat např. podtřidu typu int, která má nějakou naprosto neočekávanou vlastnost. (To je podobně neočekávané asi jako arteriální kanylou v tříslech strkat nějakou sondu do srdce. Je dobré takovou věc po použití zase odstranit.)
5. 6. 2020, 10:44 editováno autorem komentáře