[python] (ne)logičnost .join() ?

Petr Přikryl prikryl na atlas.cz
Pondělí Únor 28 15:38:41 CET 2011


Trochu popřehazuji pořadí...

"Radek" <rad... na gmail.com> napsal...
> On 23 ún, 08:08, "Petr Přikryl" <prik... na atlas.cz> wrote:
>> A co se týká "volně rozhozených built-in funkcí",
>> ono jich zase tak strašně moc není. Nezvyklé je
>> to hlavně pro lidi, kteří pracovali v čistě OO
>> jazyce, který funkce nepodporuje
> 
> jo, asi tak nějak. Teď by bodl dokument: "Jak
> zobjektit Python v místech kde se tomu brání,
> resp. kde to ještě nikdo neudělal."
> 
> To je asi to nejtrefnější: Přesně v místech kdy
> Python nesleduje objektové principy to bolí, a
> strašně.

Ono je trošku problematické říci, že Python nesleduje
objektové principy. V Pythonu je totiž objektem všechno.
Podle mého názoru dogma čistě objektového přístupu, kdy je
veškerá funkčnost realizována metodami objektů, neodpovídá
realitě, která nás obklopuje. Neodpovídá ani matematickým
abstrakcím, které nás obklopují od základní školy.

Pokud vás tedy něco "strašně bolí", pak je to porušení oněch
dogmat, které byste si přál bezhlavě následovat.

Metody tříd jsou formálním způsobem popisu chování objektů
dané třídy. Vychází se z poznatku, že při formálním zápisu
transformace vnitřního stavu objektu uděláme méně chyb, když
popis spojíme s abstrakcí objektu dané třídy. Vychází to ze
zkušenosti, že například v určitém kontextu se celé číslo 
musí chápat jako letopočet a ne jako počet kusů něčeho nebo
jako zcela abstraktní číslo. Až potud nelze nic namítat.

Existují ale situace, kdy chceme s určitou hodnotou pracovat
jako s velmi abstraktní hodnotou -- jaksi "bez kontextu" --,
kdy jí chceme interpretaci přidělit až v okamžiku
zpracování. Dám příklad. Mnohé reálné objekty lze v určitých
situacích popsat vektorem v trojrozměrném prostoru. 
Přirozeným způsobem můžeme dojít k
nějakému dejme tomu geometrickému "úhlu", který je prostě
úhlem bez bližší vazby na konkrétní objekt. Dejme tomu, že
na daný úhel potřebujeme aplikovat to, čemu od základní
školy říkáme funkce sinus. V takové situaci je velmi obtížné
považovat zápis

  objekt.vektor.úhel.sin()
  
za přirozené vyjádření toho, co nás u onoho úhlu daného
objektu zajímá. Mnohem přirozenější pro nás je zápis, kdy se
úhel stane argumentem obecné funkce sin().

>> Každý má jiný pohled na věc a jiná očekávání.
>> Podle mého názoru je přístup typu
>> ','.join(kontejner) možná na první pohled
>> nezvyklý, ale na druhý pohled je velmi logický.
>> Jde o to, že dokáže spojit zadaným řetězcem prvky
>> dodané jakýmkoliv kontejnerem, který podporuje
>> iteraci a který vrací řetězcové prvky.
> 
> Zajisté, pro implemetátora má smysl to definovat
> na jednom místě. Takže by byl dobrý mixin nebo
> třída která implementuje metody nad iterabilními
> kontejnery, a ty iterabilní kontejnery podědí nebo
> si ty metody mixnou.

Tohle je jen technický detail, který umožní svázat nějakou 
operaci s nějakou třídou objektu. Pořád bychom si měli
uvědomovat, že tečková notace používaná u OO jazyků je pouze
notací, která má usnadnit vnímání náležitosti metod k
objektům. Taková notace je přínosná v situacích, kdy máme
pocit, že je její přínos přirozený. Tím mám na mysli to, že
zápis pokud možno snadno pochopí i ten (se stručným návodem),
kdo takový zápis vidí poprvé.

> Ale když pracujete tak chcete psat něco jako
> 
> neco.jeho_kolekce.transform.sort.jeste_transform.join

Tohle považuji za poměrně nešťastné v jakémkoliv OO jazyce.
Přesněji, netýká se to jazyka, ale jeho používání. Nezávisle
na tom, zda se jedná o reference nebo ukazatele... Pokud má
transformace join probíhat na konci uvedeného řetězu, pak
musí být zajištěno, že předchozí transformace nějak
neselžou. To z hlediska robustnosti kódu nemusí být tak
samozřejmé.

> a nemáte chuť psát
> 
> join(neco.jeho_kolekce.transform.sort.jeste_transform)

Tady je potřeba si uvědomit, že metoda str.join(), o které
se tady bavíme, je velmi specifická v tom smyslu, že jde o
metodu, která má spojovat několik podřetězců do jednoho
řetězce. Je tedy specifická v tom, že pracuje nad "řetězci"
ve velmi abstraktní podobě a že výsledkem je zase čistý
"řetězec". V souvislosti s výše uvedeným příkladem úhlu a
funkce sinus je to něco podobného.

> obzvláště když ten řetězec chcete na tečce dále
> zpracovávat.
> 
> neco.jeho_kolekce.transform.sort.jeste_transform
> .join.strip.jiny_transform.split

Přehlédl jste ale jeden detail. Na začátku celé transformace
vystupuje objekt nějakého typu, který se postupně
transformuje do kolekce řetězců. Potom následuje změna velmi
specifické kolekce řetězců na velmi abstraktní typ
"řetězec". Další operace probíhají nad abstraktním řetězcem.

> když to pak přepíšete do závorkové notace, tak je
> to nečitelné, protože v závorkové notaci se píše
> doleva a v tečkové doprava. Pár takových závorek v
> tečkovaném výrazu a nevyzná se v tom ani zvíře
> které to má v popisu práce.

Tohle není věc tečkové/závorkové notace. To je věc
přehledného zápisu programu.

> A nebo jinak. Kolikrát v životě jste potřebovali
> aplikovat join na řetězec v tečce? Já si
> nepamatuju jediný případ.

Osobně mi kroutí mozkem vyjádření "aplikovat join na řetězec
v tečce". Ale přitom právě zápis

    'separátor'.join(kolekce)
    
je velmi objektový. Velmi typicky se používá například pro
vytvoření víceřádkového řetězce ze seznamu řádků, které
neobsahují n:

    'n'.join(lst)
    
nebo pro vytvoření "čitelného" řádku textových reprezentací 
hodnot, které se mají oddělit čárkou a mezerou:

    ', '.join(lst)
    
A dalších příkladů je celá řada.    

Problém je taky v tom, co by měl obecný .join() pro kolekce
vyjadřovat. Jakého typu má být výsledek?

P.


Další informace o konferenci Python