Python, wcięcia i bardzo wredny błąd

2010-09-04 17:13

Niektórzy programiści, rozmawiając z innymi, znajdują przyjemność we wskazywaniu na specyficzne cechy różnych języków programowania, które im nie odpowiadają. Nie uważam tego za specjalnie produktywne i samemu staram się tego unikać. W końcu co za różnica, że w Javie czy C++ mamy nawiasy klamrowe, w Pascalu begin i end, a w Pythonie bloki wyróżniane wcięciami? Dla sprawnego programisty nie powinno to mieć żadnego znaczenia.
I faktycznie jest – dopóki nie okaże się, że pozornie nieistotna cecha języka prowadzi do błędów. Nieprzyjemnych, wrednych i trudnych do wykrycia błędów. Tak, wtedy dyskutowanie o podobnych składniowych błahostkach może być choć częściowo usprawiedliwione…

A skoro sam ten temat podjąłem, to wymaga mi przedstawić moje usprawiedliwienie. Nie jest ono długie. Wygląda bowiem tak:

  1. def escape_chars(text):
  2.     result = []
  3.     for c in text:
  4.         if ord(c) > 255:
  5.             result.append ("&#%s;" % ord(c))
  6.     else:
  7.         result.append (c)
  8.     return ''.join(result)

Ta prosta pythonowa funkcja ma za zadanie zamienić w podanym tekście znaki spoza zakresu ANSI na ich XML-owe odpowiedniki w postaci encji numerycznych (np. Lj). Niezbyt skomplikowane, prawda? A jednak całkiem długo zajęło mi dojście do tego, czemu dla tekstu z samymi znakami ANSI wynikiem jest… łańcuch pusty.

Jest oczywiście bardzo prawdopodobne, że ktoś patrząc teraz na powyższy kod znajdzie przyczynę w mniej niż dziesięć sekund – zwłaszcza, że niemal bezpośrednio zasugerowałem ją na samym początku. To naturalnie nie świadczy o niczym, bo napady specyficznego rodzaju ślepoty na rzeczy oczywiste są nieodłączną częścią zajęcia zwanego programowaniem :) Z tym nie ma sensu polemizować.
Ale jak najbardziej można dyskutować o tym, dlaczego “wrodzona” cecha składni języka uważanego za nieskomplikowany, efektywny i nowoczesny (cokolwiek to znaczy) może być bezpośrednią przyczyną powstawania błędów, o którym użytkownikom C, Javy czy Pascala nawet się nie śniło! Nie widzę innego wytłumaczenia oprócz krótkowzroczności projektantów języka, którzy nie potrafili uświadomić sobie, że jego feature‘y mogą wchodzić ze sobą nie tylko w pożyteczne, ale czasem i niepożądane interakcje.

Dla jasności wytłumaczę jeszcze dokładniej, w czym rzecz. Mianowicie w Pythonie koniec bloku kodu rozpoznawany jest nie obecnością terminatora w rodzaju } czy end, lecz zmianą poziomu wcięcia następnej linijki – czyli czymś, co w innych językach pełni rolę wyłącznie estetyczną. Wiersze mające tę samą liczbę początkowych spacji leżą więc na tym samym poziomie zagłębienia.
Stąd zaś wynika fakt, iż w powyższej funkcji fraza else nie jest wcale dołączona do instrukcji if, lecz do… pętli for. Prawidłowe zagnieżdżenie wygląda bowiem tak:

  1. def escape_chars(text):
  2.     result = []
  3.     for c in text:
  4.         if ord(c) > 255:
  5.             result.append ("&#%s;" % ord(c))
  6.         else:
  7.             result.append (c)
  8.     return ''.join(result)

Ale chwilka – jak pętla for może mieć else‘a?… Ano to już jest rzecz specyficznie pythonowska (a biorąc pod uwagę okoliczności, wręcz monty-pythonowska), którą zresztą kiedyś zdarzyło mi się opisać. Wówczas to określiłem ją tylko jako zawracanie głowy. Dzisiaj porównałbym ją raczej z możliwością wpisywania stałych ósemkowych w kodzie C. Oba feature‘y używane są świadomie średnio raz na trzy lata, przez resztę czasu potencjalne powodując jedynie błędy.

Tags: , ,
Author: Xion, posted under Programming »


9 comments for post “Python, wcięcia i bardzo wredny błąd”.
  1. TeMPOraL:
    September 4th, 2010 o 19:43

    Osobiście dla mnie wcięcie takie jak w pierwszym przykładzie (else na poziomie for’a) jest równie rażące w C++ co w Pythonie – po prostu widać, że w kodzie jest coś źle ;).

  2. michalp:
    September 4th, 2010 o 20:08

    Między innymi dlatego warto pisać testy jednostkowe. A co mi się podoba w pythonie to, że ma wbudowaną bibliotekę unittest i doctest.

  3. vashpan:
    September 4th, 2010 o 21:19

    Heh, wlasnie pamietajac twoj wpis o Pythonowskim ‘elsie’ dla for’a od razu wylapalem ten niuans, ale byc moze tez pomogla rzeczywiscie sugestia o wcieciach ;)

    Co do testow jednostkowych, jeszcze ‘fajniej’ jest w D, ktory nie potrzebuje biblioteki gdyz jezyk sam w sobie zapewnia wsparcie na testy, ( poza innymi ‘fajnymi’ sprawami w tym jezyku ) Szkoda tylko ze narazie bez porzadnego debuggera i IDE nie bardzo da rade sie go uzywac…

  4. MSM:
    September 4th, 2010 o 22:31

    Ja również zgadłem rozwiązanie w kilka sekund, ale zdarzało mi się siedzieć godzinami nad głupszymi rzeczami (np. zamiast 1 w kodzie była wredna jednoliterkowa zmienna ‘l’ co czcionka domyślna sprytnie ukrywała) więc rozumiem o co chodzi.

    Btw – ja osobiście jestem neurtalny co do else do pętli for, ale czytałem kilka opinii chwalących to rozwiązanie (że niby upraszcza pisanie w niektórych przypadkach)

  5. Anonymous:
    September 6th, 2010 o 20:38

    W tym podlinkowanym wpisie jest:
    bool IsPrime(unsigned x)
    {
    for (unsigned i = 0; i <x; ++i)
    if (x % i == 0) return false;
    return true;
    }

    Ja tu widzę dzielenie przez 0 :)

  6. lukaszw:
    September 6th, 2010 o 20:39

    Heh, zapomniałem nicku wpisać :D

  7. Xion:
    September 7th, 2010 o 0:38

    Tru! Poprawiłem, dzięki :)

  8. TeWu:
    September 8th, 2010 o 4:36

    Myślę, że ten błąd byłby tak samo trudny do wykrycia w C czy Javie (jak napadnie kogoś “ślepota” to i w jakim by języku nie programował i tak będzie się męczył) gdyby nie to że pętle w Pythonie mogą mieć else’y – dodanie tego featureu faktycznie tylko pozwala na powstawanie takich błędów, podczas gdy w C czy Javie program by się nawet nie skompilował :)

    Osobiście nigdy nie potrafiłem CZYTAĆ (a to ważne) kodu bez normalniej określonych granic bloków, dlatego też od pythona staram się stronić :P

  9. Ezo:
    September 1st, 2012 o 8:23

    Nie znam pythona(ale o takich rzeczach jak wciecia zamiast klamer, i nawet to else do fora wiem), a odrazu pomyslalem ze blad bedzie przy tym elsie :P

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.