Můj názor:
Dynamické jazyky se od statických liší tím, že mohu (snadno) za běhu měnit struktury. Příklad:
class X:
def config(self):
def foo():
print("Great")
self.foo = foo
def sayGreat(fooable):
fooable.foo
x = X()
# špatně
sayGreat(x) # výjimka
# správně
x.config()
sayGreat(x)
Z čehož jsem si vyvodil tyto závěry:
1. Výše uvedené chování je v jiných jazycích (C#, Java, PHP) považováno za antipattern (přestože se hojně používá - viz oblíbené flamewar settery versus konstruktor). V jazycích jako Haskell to dokonce skoro ani nejde (schválně).
2. To, na co mají některé jazyky typy, na to má Python testy. Když se v Javě rozhodnete měnit nějakou věc, musíte často změnit spoustu typů. Když v Pythonu chcete změnit nějakou věc, musíte s ní i zohlednit testy.
3. V Javě, nebo v lepších jazycích postavených na typech (Haskell), když se konečně prokoušete všemi těmi změnami, a kompiler vám konečně úspěšně přeloží projekt, máte z velké části vyhráno, a apka často běží jak má. V dynamických jazycích ušetříte spousty času tím, že to velice snadno napíšete, pak to spustíte, a začnou vám padat výjimky víše uvedeného druhu. Což podle typu aplikace, kde se musíte složitě proklikat ke konkrétnímu stavu může být docela dost bolestivé.
Když si vypůjčím přirovnání od @Ivan Nového, tak je mi sympatičtější, když mě compiler vynadá, že jsem si dal boty na ruce už doma, než že to zjistím až před barákem, když se pokouším odemknout dveře od auta.
4. V ničem jiném zásadnějším se dynamické od statický jazyků neliší. Jen jednu věc řeší jinak.
Zjistil jsem, že okenní aplikace, kde musím udělat víc jak pět kroků, abych dosáhl stavu a otestoval to je na Python příliš velká aplikace. A psát unittesty na to, zda se atribut jmenuje `vals`, `values`, `items` abych si ověřil, že jsem to napsal dobře, mě nebaví.
Zjistil jsem, že pro víše uvedenou dynamičnost nemám využití. Naopak oceňuji, když mi compiler najde alespoň 50% chyb dříve, než to začnu testovat - nebo nedejbože to jde do produkce.