Python, wrażenia

Posted by Michał ‘mina86’ Nazarewicz on 31st of January 2010

Niedawno miałem okazję stworzyć trochę kodu w Pythonie. Popisałem sobie w nim kilka dni i muszę przyznać… język jest tragiczny, zastanowię się kilka razy zanim po raz kolejny go użyję.

Wpis ten dedykuję pewnej osobie, która ciągle mi przypomina, abym pisał nowe wpisy. :)

Wcięcia

Zacznę powolutku, od kwestii raczej mało istotnych, wręcz błahych, zatem na początek może o wcięciach. Jak niektórzy pewnie wiedzą, Python wyróżnia bloki za pomocą wcięć, czego efektów jest kilka.

Po pierwsze edytorowi tylko czasami udaje się zgadnąć poziom wcięcia. W „zwykłych” językach, wystarczy dopasować do siebie klamerki (czy inne wyznaczniki bloku), aby móc bezbłędnie wskazać, w którym miejscu powinien zaczynać się tekst. Gdy jednak blok jest wyróżniony wcięciem, to w najlepszym wypadku, edytor będzie pozwalał na wielokrotne wciskanie taba, co spowoduje skakanie pomiędzy możliwymi poziomami wcięć.

Po drugie, choć to może kwestia przyzwyczajenia, ale zmiana poziomu zagnieżdżenia dla większych fragmentów kodu jest zadaniem nietrywialnym.

Po trzecie, jak powszechnie wiadomo „najlepszym” sposobem debugowania jest dodanie printów wewnątrz kodu. Mam zwyczaj pisać je od początku linii, dzięki czemu rzucają się w oczy i łatwo je potem usunąć. W Pythonie coś takiego nie przejdzie — wywołania muszą być wyrównane, co skutecznie komplikuje późniejsze wyczyszczenie z nich kodu.

Upośledzona lambda

Pozostając jeszcze w strefie składni i wcięć, warto wspomnieć o upośledzonym wyrażeniu lambda. W Pythonie bowiem, wyrażenie lambda owszem istnieje, ale może się składać tylko z pojedynczego wyrażenia.

Nie to, żeby mi to jakoś szczególnie przeszkadzało, no ale szkoda…

Komentarze

Przejdźmy jednak do następnego punktu. Ponownie coś mało istotnego, co jest zresztą cechą wielu języków skryptowych. Otóż w Pythonie nie ma komentarzy blokowych. Jest to złe z co najmniej dwóch powodów.

Po pierwsze, nie ma łatwego sposobu (bez wykorzystania rozbudowanego edytora) na zakomentowanie dużego fragmentu kodu, szczególnie, że dodanie if False: na początku wymaga dokładnie takiej samej pracy (dodania wcięcia zamiast hasha).

Drugą wadą komentarzy liniowych (jeśli można je tak nazwać) jest fakt, że wszystko się psuje jeżeli wiersz zostanie zawinięty. I uwierzcie mi, zdarza się to nazbyt często na grupach czy w korespondencji mailowej.

Osobiście sądzę, iż Ritchie miał tutaj rację — sam komentarz blokowy w zupełności wystarcza, gdy tymczasem sam komentarz liniowy to zdecydowanie za mało.

Magiczny przecinek

Niemniej, moje ulubione dziwactwo, na które się natknąłem w składni Pythona to „magiczny przecinek” jak go zacząłem nazywać. Otóż poniższe dwie linijki są jak najbardziej poprawnym kodem Pythona i znaczą coś zupełnie innego:

[ "a", "b" ]
[ "a"  "b" ]

Fajnie, nie? Żeby połączyć ze sobą dwa literały znakowe wystarczy napisać jeden obok drugiego bez żadnego operatora. Doprawdy boskie, a przynajmniej takie się wydaje do momenty gdy kilka razy o tym przecinku zapomnimy…

Krotki też dodają dodatkowej zabawy, no bo czy nie jest wyborne, że poniższa linijka jest jak najbardziej poprawnym kodem Pythona:

"a",

I bynajmniej, jej wynikiem nie jest ciąg znaków, a jednoelementowa krotka składająca się z ciągu znaków. Jakby nie można było w gramatyce zawrzeć, że krotki muszą być zawsze otoczone w nawiasy, o ile jaśniejsza byłby wówczas zapis i o ile trudniej o literówki.

(Zresztą idea tego typu danych też trochę mi umknęła, bo po co krotka skoro są tablice? Tak wiem, krotka nie może ulec zmianie, ale co z tego? No ale, Pythonem bawiłem się stosunkowo krótko, więc nie wnikam — wierzę, że jakiś powód na dodanie krotek był).

Na zakończenie o magicznym przecinku wspomnę jeszcze o magicznym princie. Print bowiem nie tylko oddziela argumenty spacją (przez co jeżeli chcesz wypisać dwie wartości bez odstępu musisz je jawnie skleić), ale dodaje na końcu znak przejścia do nowej linii. Co zrobić, jeżeli go nie chcemy? Magiczny przecinek przybywa na ratunek:

print "hello,",
print "world"

do-while

Czas odejść od spraw błahych i zająć się czymś poważniejszym. Zacznę od pętli do-while, a dokładniej jej braku.

W pierwszej chwili sądziłem, że to jakieś przeoczenie, że w tutorialu nie wspomnieli o do-while, ale nie… Python czegoś takiego zwyczajnie nie ma. Ciągle się zastanawiam jak można stworzyć język bez tak oczywistej konstrukcji. Jest to jedyny znany mi nieezoteryczny język imperatywny, w której takiej pętli nie ma.

W trakcie googlania spotkałem się ze stwierdzeniem, że każda przemyślana pętla może być zapisana w postaci pętli while — w tym momencie zacząłem się zastanawiać, czy gość, który to napisał, ma pojęcia o czym mówi, czy może uważa, że while True: … if not warunek: break to naprawdę ładna konstrukcja. Pocieszałem się tylko tym, że pewnie był to jakiś domorosły ekspert nie związany z tworzeniem Pythona.

Widoczność pól klasy

W 2004 roku wydany został PHP 5. Wspominam o tym dlatego, że jedną ze zmian w stosunku do PHP 4 było dodanie modyfikatorów zasięgu (public, privateprotected). Tymczasem Python, mimo iż jest językiem starszym przez co wydawałoby się dojrzalszym, ciągle takich mechanizmów nie ma (przynajmniej w 2.6) i nadal opiera się na umowie „podkreślenie jest niepubliczne”.

Dynamiczne typowanie i deklarowanie zmiennych

I tak oto doszliśmy do najpoważniejszego moim zdaniem zarzutu. Winny jednak jestem słówko wyjaśnienia, bo niby co złego jest w dynamicznym typowaniu i braku deklaracji zmiennych? Przecież to upraszcza pisanie programów!

Rzeczywiście, upraszcza… pisanie stulinijkowych skryptów, ale gdy tylko zaczynamy pisać coś większe przeradza się w ogromną wadę. Wystarczy porównać te dwa skrypty, jeden napisany w Pythonie, a drugi w, tak przez wszystkich znienawidzonym, Perlu:

import random

name = "Jane Doe"
if random.randint(0, 99) == 0:
    print "Hello,", nmae
else:
    print "Hi!"
use warnings;
use strict;

my $name = "Jane Doe";
if (rand(100) < 1) {
    print "Hello, ", $nmae, "\n";
} else {
    print "Hi!\n";
}

Interpreter Pythona bez zmrużenia oka wykona podany skrypt i z 99% prawdopodobieństwem nie dowiemy się, że mamy literówkę. Tymczasem perl wyłoży się w momencie kompilacji (nawet bez strict dostaniemy ostrzeżenie).

Zupełnie analogiczny przykład można stworzyć na wadę dynamicznego typowania:

import random

if random.randint(0, 99) == 0:
    foo = "10"
else:
    foo = 10

print foo + 10

Tymi prostymi przykładami chcę pokazać, że dynamiczne typowanie i brak deklarowania zmiennych nie jest wcale czymś korzystnym. Efekt jest taki, że wiele błędów, które kompilator mógłby wykryć ujawniają się dopiero w czasie wykonania (a potem jest wielkie zdziwienie, gdy coś się wysypuje w trakcie prezentacji produktu na międzynarodowej konferencji).

Z tych powodów, miałem nadzieję, że w nowych językach, twórcy będą już od tego odchodzić i tak samo się stanie w Pythonie, ale niestety jego twórcy postanowili brnąć w tą ślepą uliczkę… Szkoda.

Posłowie

Gdy zaczynałem pisać projekt to do języka podchodziłem z entuzjazmem — wcięcia co prawda zawsze mnie odstraszały, ale tak go wszyscy zachwalali, że myślałem sobie, że może faktycznie jest taki wspaniały. Niestety zawiodłem się.

Pozostaje mi jedynie czekać na wydanie Perla 6.