Edit detail for RozhovorCast4 revision 1 of 1

1
Editor: pycz
Time: 2005/10/05 08:35:06 GMT+0
Note:

changed:
-
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 <mailto:honza(at)py(dot)cz>`_
:Date: 2005-10-05
:Copyright: Copyright (C) 2004-2005 `Jan Švec <mailto:honza(at)py(dot)cz>`_


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
=====
 + RozhovorCast1 - Historie Pythonu
 + RozhovorCast2 - Cíle a návrh Pythonu
 + RozhovorCast3 - Rapidní vývoj v Pythonu
 + RozhovorCast4 - Závazky v programování
 + RozhovorCast5 - Statické vs. dynamické typování
 + RozhovorCast6 - Veřejné API jazyka Python



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