Rozhovor s Guido van Rossumem, část 4.

Autor jazyka Python Guido van Rossum odpovídá na otázky Billa Vennera o historii Pythonu, o vlivu jazyka ABC na vývoj Pythonu a o hlavních cílech při vývoji Pythonu.

Originál rozhovoru je dostupný na http://www.artima.com/intv/guido.html .

Author:Jan Švec
Date:2005-10-05
Copyright:Copyright (C) 2004-2005 Jan Švec

Metody a "vazby"

Bill Venners: Jedním z přínosů objektově orientovaného programování je oddělení rozhraní a implementace. Protože klientův kód je svázán pouze s rohraním, mohu libovolně měnit implementaci metod nebo soukromých dat. To mohu provádět aniž bych změnil funkčnost již existujícího kódu. Klientův kód je svázán jen s definicí metody, která defacto vytváří ono rozhraní. Proto je oddělení rozhraní a implementace metod technikou, která umožňuje změny v kódu metodu a zaručuje nulové provázání mezi soukromým kódem metody a kódem klienta.

To ale vyžaduje jak od programátora, který definuje rozhraní a implementuje metody, tak od programátora, jenž píše klientský kód, aby věděli, co která metoda s daným rozhraním provádí. Rozhraní tedy znamená vazbu mezi kódem a implementací rozhraní. Jestliže metoda <code>add</code> sčítá dvě čísla, mohu upravit její kód za účelem efektivnější implementace, samotná metoda ale stále sčítat! Pokud začne jedno číslo odčítat od druhého, přestane veškerý kód používající tuto metodu pracovat správně, přestože překlad tohoto kódu proběhne v pořádku.

Ve volně typovém prostředí, jakým Python je, nemají proměnné žádný typ. Napíši-li metodu, jež bude přebírat dva parametry, x a y a za běhu zavolám nějakou metodu parametru x, bude vše bez problémů, pokud objekt odkazovaný tímto parametrem bude mít takto deklarovanou metodu. Nemusím však ale nutně vědět, co tato metoda provádí.

V silně typových jazycích má každá proměnná typ známý již v čase překladu. Pokud se pokusím zavolat metodu nějaké proměnné, ale její typ tuto metodu nedeklaruje, překladač ohlásí chybu při překladu. To je rozdíl v chování volně (též slabě) a silně typových jazyků. V silně typových jazycích přijdu na některé chyby již v čase překladu, zatímco ve volně typovém prostředí budu doufat, že případnou chybu objevím za běhu programu. V silně typových jazycích typ proměnné popisuje její rozhraní -- rozhraní objektu. Toto rozhraní určuje nejen, které metody konkrétní objekt deklaruje, ale také význam jednotlivých metod.

Například dva objekty Artista a Pistolnik budou mít metodu deklarovanou jako void kresli(). Tato metoda objektu Artista nakreslí cvičícího človíčka zatímco táž metoda objektu Pistolnik nakreslí jeho střílející pistole. V silně typovém jazyce si dokážu udělat představu o významu jednotlivých volání (tj. zda objekt bude kreslit figurky nebo pistole) pouhým pohledem na typ proměnné. Znám-li typ objektu, znám i význam metody kresli. V jazycích jako je Python, které objektům přiřazují typ až za běhu programu, lze také zjistit význam kresli pomocí typu objektu, který získám za běhu programu, což se v praxi příliš nepoužívá. V Pythonu totiž nikdy nemusíte kontrolovat typy objektů, pouze zavoláte metodu kresli, které přiřadíte nějaký konkrétní význam. Tímto "domyšlením" významu ovšem ztratíte záruku, že se opravdu provede to, co očekáváte.

Proč to v praxi funguje? Proč programy v Pythonu pracují, když nelze zjistit, která konkrétní metoda se zavolá při vykonání programu?

Guido van Rossum: To mi připadá jako neopodstatněný strach. Z mých zkušeností je navržení rozhraní často ta nejtěžší část při práci na programu. Flexibilní návrh, umožňující změny implementace objektu bez změn jeho rozhraní, se uplatní pouze v určitých oblastech. Pokud potřebujete seřadit poštovní směrovací čísla, můžete například napsat jednoduchý řadicí algoritmus. Pokud později zjistíte, že vaše původní implementace není dostatečně rychlá, můžete na ní dále pracovat. To je klasický způsob použití rozhraní.

Ale v mnoha situacích se po navržení rozhraní zjistí, například při práci na další verzi programu, že je navrženo nevhodně. Některé potřebné informace jsou například drženy jako soukromé, případně některá data nikdy neopustí metodu, přestože by se hodila jejímu klientovi, některá jsou data nadbytečná atd.

Python má implicitní vazby

Bill Venners: Podle mé teorie se v Pythonu v 99 procentech všech funkčních volání vykoná přesně to, co očekávám. Jestliže metodu zavolám v silně typovém jazyce, ve 1 procentu případů může chyba nebo špatné pochopení rozhraní této metody způsobit, že se vykoná něco nepředvídatelného. Přestože v silných jazycích je každá vazba odvozená od typu proměnné, neznamená to, že lidé tyto vazby neporušují. Vazby často se porušují díky chybám a nepochopením rozhraní. Myslím si, že jak v silně, tak ve slabě typových jazycích, je frekvence výskytu situací, kdy metoda dělá něco nepředvídatelného, úplně stejná a většina chyb může být odkryta a opravena již během testování.

Guido van Rossum: Předám-li v Pythonu argument nějaké metodě, nevím, co je tento argument zač. Předpokládám však, že podporuje metodu readline, zavolám ji tedy. Nyní předpokládejme, že objekt tuto metodu nepodporuje...

Bill Venners: ... dostanu výjimku ...

Guido van Rossum: Dostanete výjimku, což je pravděpodobně v pořádku. Jestliže se jedná o hlavní část kódu a někdo mu předá objekt, který nemá metodu readline, zjistíte to již během testování tohoto kódu. Stejně jako v typových jazycích v případě, kdy určitý objekt nevyhovuje požadovanému rozhraní. Podobně během testování objevíte i výjimky, které se z vašeho kódu neočekávaně šíří.

V Pythonu však můžete díky tomu, že postrádá fixní protokoly, předat i jakýkoli jiný objekt implementující metodu readline a nemusí to být jen soubor, ale klidně váš vlastní objekt provádějící specifickou činnost. Jediné, co potřebujete je, aby tento objekt vracel jednotlivé řádky stejně jako metoda readline souborového objektu.

Bill Venners: Ale to také mohu předat cokoli, co sice má metodu readline, ale provádějící něco úplně jiného.

Guido van Rossum: V Pythonu obecně vazby existují, ale nejsou implicitní. Vazba není specifikována rozhraním. Neexistují typy, podle nichž by se parser rozhodoval, může pouze přinejlepším říci, že objekt x podporuje metodu readline, kterou můžete volat bez argumentů a vrací řetězec znamenající určitou věc. Vazbu ale konkrétně určuje pouze specifikace nebo dokumentace.

Řeknete-li v Javě, že něco má metodu readline, která vrací řetězec, co to znamená? Předpokládáte, že vrací stále stejný řetězec? Může vrátit prázdný řetězec? Mnoho věcí vám však rozhraní nezaručí, musí se blíže specifikovat právě v dokumentaci.

Je kód vazbou?

Bill Venners: V knize Learning Python (O'Reilly, 1999) Mark Lutz a David Ascher vykládají metody a uvádějí následující příklad, metodu times(x, y), která vrátí x * y:

>>> def times(x, y):
...    return x * y
...

Autoři pak ukáží dva příklady vyvolání metody times, jeden který používá dva integery a jeden používající řetězec a integer:

>>> times(2, 4)
8
>>> times('Ni', 4)
'NiNiNiNi'

Jinými slovy, pokud jí předáte čísla 2 a 4, metoda times vrátí 8. Když je předán řetězec 'Ni' a číslo 4, pak times vrátí 'NiNiNiNi?', jelikož operátor * aplikovaný na posloupnosti (například řetězce nebo seznamy) znamená opakování této posloupnosti. Autoři to komentují slovy: "Připomeňme si, že * pracuje jak na číslech, tak na posloupnostech; protože v metodách neexistuje deklarace typů, můžete používat times jak na vynásobení čísel, tak pro opakování posloupností."

Nedělá neexistence deklarování typů parametrů jednotlivých metod změny kódu těžšími, obzvláště pak ve standardních knihovnách? Lidé přece mohou definovat novou třídu a v ní přetížit operátor * tak, že bude provádět cokoli jiného. Poté mohou předat instanci této třídy metodě times a použít tak metodu times způsobem, který její autor nikdy neuvažoval, což je způsobeno změnou významu operátoru *. Jestliže mohu změnit způsob, jakým metoda times pracuje, mohu přece způsobit její chybnou funkci. Není pak vazbou vlastně soukromý kód metody, který by měl zůstat skrytý?

Guido van Rossum: Na začátek, pokud píšete standardní knihovnu, je těžké měnit metody nebo operace, které používáte na argumenty předávané metodám. Nemyslím si, že je to specifikum volně typových jazyků.

To, co klient metodě předá, čili ona vazba, je mnohem více omezena v silně typových jazycích. Tento způsob omezení je někdy výhodný, poněvadž se vám zužuje množina rozhraní, kterou je třeba uvažovat. Vše se ale stane obtížnější, když se v příští verzi rozhodnete používat více vlastností určitého objektu, které však zatím nejsou součástí jeho rozhraní. Pokud nechcete změnit rozhraní vaší metody, stane se pro vás změna nemožnou.

Obsah