Skocz do…
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. :)
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.
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…
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.
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"
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.
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, private i protected). 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”.
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.
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.
W kategoriach:
Słowa kluczowe:
KomentarzeMimo wszystko to chyba nadal jest przyjemniejsze niż Java?
Wcięcia... Wystarczy poprawnie skonfigurowany vim ;)
Lambdy: Jeżeli chcesz tworzyć inline skomplikowane funkcje, to współczuję maintainerowi kodu. Jeżeli chcesz robić coś bardziej skomplikowanego, to deklarujesz funkcję linijkę wcześniej - klasy i funkcje można deklarować w klasach i funkcjach.
Przecinek raz dał mi się we znaki, gdy zamieniałem słownik na listę zmiennych - zapomniałem pousuwać przecinki. Konkatenacja literałów jak najbardziej ma dla mnie sens, chociażby w przypadku gdy chcesz ładnie połamać tekst na kilka linii.
Print w wersji 3.0 jest już funkcją, mniej magii.
Pętle: Po co tworzyć nadmiarowe konstrukcje które można bez żadnego wysiłku zastąpić innymi?
Zmienne prywatne/chronione - po co to komu?
Typowanie dynamiczne: jeśli się nie pilnujesz, to oczywiście że się wali. W C też trzeba się pilnować. Jak źle kombinujesz ze wskaźnikami, kompilator ci tego nie wyłapie, a po miesiącu od release okazuje się że segfaultuje w przypadku X. Do czego są unit testy? Poza tym dynamiczne typowanie rodzi szereg innych możliwości.
Remigiusz, to są zupełnie inne języku, raczej trudno je porównać.
Fluxid, i czy poprawnie skompilowany vim automagicznie zgadnie poziom wcięcia dla kolejnej linijki kodu w tym przykładzie:
if foo:
bar()
if baz:
qux()
if fred:
barney()
Zresztą tak jak pisałem, wcięcia są dla mnie akurat najmniej istotne, więc nie mam zamiaru o nie kopii kruszyć.
Lambdy: no bo przecież dwie linijki to już skomplikowana funkcja...
Łączenie literałów: no bo przecież nie można użyć plusa.
Pętle: Po co w takim razie powstała pętla for? Przecież można ją zrealizować za pomocą while. Zresztą to zabawne, że z jednej strony w Pythonie nie ma goto i tak wszyscy na nią złorzeczą, ale z drugiej strony najlepszym sposobem stworzenia pętli do-while jest wykorzystanie instrukcji break.
Typowanie: Czyli fakt, że w C korzystając ze wskaźników trzeba się pilnować tłumaczy, czemu trzeba się pilnować w Pythonie? Doprawdy, pokrętna logika.
Tymczasem ten szereg możliwości jakie daje dynamiczne typowanie można w prosty sposób osiągnąć wprowadzając typ "Any" (czy coś analogicznego) i wówczas wszędzie tam, gdzie byłoby to coś korzystnego można by z tego skorzystać, przy jednoczesnym sprawdzaniu poprawności typów przez kompilator we wszystkich innych miejscach.
Zgadzam się z Fluxidem, a od siebie dodam tylko że:
1. Dobry edytor (dla mnie: eclipse, ale nie jestem zbyt rozmoiłowany w konsoli więc można woleć vi ;]) do pythona bardzo ułatwia życie. W szczególności debug, komentarze blokowe, utrzymanie porządku w indention.
2. Python to nie język ogólnego przeznaczenia, tylko narzędzie do fast-and-dirty work. : ) Dlatego brak statycznego typowania to w tym segmencie zaleta.
Plus natychmiastowy dostęp do wielu bibliotek, których odnalezienie w innych językach zajmuje zwykle więcej czasu.
Otóż w Pythonie nie ma komentarzy blokowych. Jest to złe z co najmniej dwóch powodów.
Są, trzy znaki albo apostrofu, albo cudzysłowia. Tak samo można przypisać wieloliniowe teksty do zmiennej.
krzyszsz, szkoda tylko, że w języku do "fast-and-dirty work" pisze się duże serwisy internetowe i poważne programy. Zresztą tym stwierdzeniem przyznałeś rację zasadniczej tezie mojego wpisu.
Sigvatr, to nie jest komentarz tylko literał znakowy, który można zostać potraktowany jako dokumentacja i nie jest zalecane jego stosowanie do komentowania kodu.
Istnieją komentarze blokowe w Pytongu ;)
""" na początku i """ na końcu ;)
No to mnie zniechęciłeś do Pythona, a miałem się za niego zabrać. Może to i lepiej, nie będę tracił czasu?
Co do do-while to w Perlu 5 choć jest to upośledzona, bez next/last/redo. W każdym razie nie tylko ty czekasz na "szóstkę".
http://mindview.net/WebLog/log-0025 - na temat dynamicznego typowania
Sam nie jestem przekonany do pisania dużych serwisów w pythonie, to rzeczywiście trochę szalone, chyba że masz 100% test coverage. : ) No bo trochę się z Tobą zgadzam: jeśli piszesz program który ma sterować czymś co nie powinno wybuchnąć, to lepiej wykorzystać język, który wychwyci jakąś klasę błędów przez statyczne typowanie (+ potem przejechać to czymś do analizy stylu, zrobić kilka code review z zespołem, testy jednostkowe, integracyjne, etc). Ale i tak fajnie się pisze krótkie narzędzia w pythonie, dużo przyjemniej niż w shellu albo matlabie [zależnie od zastosowania]. : )
Heh, ale wiesz, to, co piszesz o dynamicznym typowaniu, to jest problem Pythona, a nie dynamicznego typowania jako takiego - można to zrobić tak, żeby znajdowało takie błędy (i inne zresztą też):
http://paste.lisp.org/display/94178
W tym kodzie są dwa błędy, kompilator wyłapał obydwa.
--> Szczała
Z częścią treści, którą zamieściłeś można się zgodzić lub nie, to oczywiście zależy od tego na czym się wychowaliśmy i do czego przywykliśmy. Oczywiście wyraziłeś swoje zdanie i bardzo dobrze.
Nie zgadzam się jednak z kilkoma fragmentami w których raczej udowodniłeś słabą jakość twojego kodu niż niedostatki w Pythonie. Na tej samej zasadzie można udowadniać, że Smalltalk ładniej wygląda od C++. Co z tego skoro dowody opierają się na nadużyciach.
A co do reszty
- łączenie literałów tekstowych w ten sposób jest w wielu językach, np. w C
- nie pamiętam, kiedy ostatnio użyłem pętli do-while
- wcięcia? no stary, tak ci ciężko wcisnąć backspace żeby przejść do poprzedniego poziomu indentacji?
- public/protected/private? po co? to rozwiązuje problem, którego nie ma.
krzyszsz, co do linku to problem w tym, że z mojego doświadczenia mając wsparcie ze strony kompilatora, który sprawdzi niektóre rzeczy za mnie, nie muszę pisać tylu testów, aby program sprawdzić. Często wręcz, nawet i duże moduły napisane w C czy C++ działają bezbłędnie, gdy tylko doprowadzę je do stanu, w którym się kompilują, a testy tylko to potwierdzają.
A co do wygody: mnie wygodnie(j) pisze się krótkie skrypty w Perlu i właśnie dlatego nie zamierzam kontynuować eksperymentów z Pythonem, jeżeli nie będzie mi do czegoś szczególnego potrzebny.
sprae, mógłbyś rozwinąć.
Dodek, łączenie literałów jest w innych językach, ale wcale nie mówię, że w tych innych językach dobrze, że tak jest zrobione.
Odnośnie pętli do-while to bardzo prosty przykład:
do {
print "Podaj cyfrę dziesiętną: ";
$_ = <>;
} while (!/^\s*\d\s*$/);
$_ = int $_;
print "Podałeś $_\n";
O wcięcia, jak już wcześniej napisałem, nie zamierzam się kłócić – wspomniałem o nich dla porządku, bo skoro wypisywałem wszystko, co mi się w Pythonie nie podoba, no to wszystko.
Z kontrolą dostępu jest natomiast taki problem, że prędzej, czy później znajdzie się ktoś, kto będzie grzebał tam gdzie nie powinien (w interfejsie prywatnym), ba tak mu wygodniej, a wystarczy dodać obsługę modyfikatorów, aby takiego cwaniaka skutecznie zniechęcić. Zresztą czasami mogą zdarzyć się błędy, czy niedopatrzenia (jakiś skopiowany kod, pozostałości po refaktoryzacji), przed którymi takie private by zabezpieczyło.
Bleh, a miałem nie komentować.
krzyszsz, szkoda tylko, że w języku do "fast-and-dirty work" pisze się duże serwisy internetowe i poważne programy.
Co dowodzi, że większość tych 'problemów' jest szukana na siłę. Osobiście mógłbym się co najwyżej zgodzić z niesprawdzaniem przez kompilator nazw zmiennych. Ale to z kolei wykrywa pylint, albo testy.
Do tymczasowego komentowania docstringi wystarczają -- kod, który ma być wykomentowany na dłużej powinien być usunięty. Po coś te systemy kontroli wersji mamy.
Debugowanie robi się debuggerami i logowaniem, nie printem. Zresztą od 2.6 jest __future__ przekształcający print statement w zwykłą funkcję. Jest też file-like sys.stdout/sys.stderr.
A tak to Dodek += 1.
Odnośnie pętli do-while to bardzo prosty przykład:
Ależ ja nie wątpię, że do-while jest przydatne - ja tylko twierdzę, że jest przydatne zbyt rzadko, żeby robić dodatkową konstrukcję.
Z kontrolą dostępu jest natomiast taki problem, że prędzej, czy później znajdzie się ktoś, kto będzie grzebał tam gdzie nie powinien
hyh, a co ciebie to obchodzi? :) jak zmodyfikuje coś tak, że twój kod przestanie działać, to on będzie potem szukał buga, nie ty, poza tym przy pierwszej aktualizacji w której zmienisz wewnętrznie działanie tego, kod mu się spierdoli i się nauczy.
Zresztą czasami mogą zdarzyć się błędy, czy niedopatrzenia (jakiś skopiowany kod, pozostałości po refaktoryzacji), przed którymi takie private by zabezpieczyło.
no to jest zadanie w sam raz dla zewnętrznych narzędzi - w mojej implementacji lispu mam who-calls, who-binds, who-references, who-macroexpand etc, i nie mam szans na niedopatrzenia - za to w języku jako takim to jest niepotrzebne
Co dowodzi, że większość tych 'problemów' jest szukana na siłę.
Ależ wręcz przeciwnie. Na wszystko co opisałem sam się naciąłem, choć w przypadku niektórych kwestii nie w przypadku Pythona.
Ale to z kolei wykrywa pylint, albo testy.
Wiadomo, testy pokrywające cały kod, wszystkie ścieżki wykonania, sprawdzą wszystko, tylko, że nie zawsze takie testy łatwo napisać, nie wspominając już o sytuacjach, gdy musisz sklecić kod z kilku innych modułów, które niekoniecznie były dobrze przetestowane.
Ja nie twierdzę, że nie należy testować. Twierdzę, że kompilator to nasz przyjaciel i powinniśmy korzystać z jego dobrodziejstw, a nie wszystko odrzucać na czas wykonania, aby mieć więcej roboty w pisaniu testów.
Do tymczasowego komentowania docstringi wystarczają -- kod, który ma być wykomentowany na dłużej powinien być usunięty.
Tutaj przyznam, że rzeczywiście takie rozwiązanie wydaje się sensowne. Co nie zmienia faktu, że klienty poczty lubią zawijać kod w nieodpowiednim miejscu.
Debugowanie robi się debuggerami i logowaniem, nie printem.
Debugowanie robi się tym co najwygodniejsze, a czasami tymczasowy print jest najwygodniejszy. Zresztą logowanie to nic innego jak print pod szumną nazwą, więc argument nadal pozostaje w mocy.
hyh, a co ciebie to obchodzi? :) jak zmodyfikuje coś tak, że twój kod przestanie działać, to on będzie potem szukał buga, nie ty, poza tym przy pierwszej aktualizacji w której zmienisz wewnętrznie działanie tego, kod mu się spierdoli i się nauczy.
Jasne... jeszcze wierzysz, że tak zawsze jest?
no to jest zadanie w sam raz dla zewnętrznych narzędzi
To jest podobna kwestia jak z dynamicznymi typami. Wg mnie powinniśmy mieć możliwość kazać kompilatorowi robić tak dużo sprawdzeń jak to tylko możliwe. Ty twierdzisz, że nie ma po co, bo są osobne narzędzia.
Przypomina to trochę sytuację zanim w C były prototypu funkcji – wtedy też było osobne narzędzie do sprawdzania, czy funkcje wołane są z dobrymi argumentami. Nie chcę powiedzieć, że jest to całkowicie analogiczna sytuacja, ale, wybaczcie, że powtórzę, uważam, że kompilator powinien sprawdzać ile się da, bo dzięki temu mamy do napisania o kilka testów mniej.
W programowaniu oprócz oczywistych ograniczeń wyznaczanych przez składnie języka mamy też dodatkowe ograniczenia stylistyczne poprawiające czytelność kodu. W Pythonie są od tego odpowiednie PEP.
W programowaniu strukturalnym mamy takie ograniczenia jak mieszczenie się bloku funkcji w obszarze 80x25, oraz zagnieżdżanie do trzech. To nie tylko wymogi fizyczne dawnych terminali, ale też rzeczywiście poprawienie jakości kodu.
Przełamuje to początkujących frustratów stawiających sobie ponad wszystko minimalizacje ilości funkcji i zmiennych (niektórzy nazywają to nawet optymalizacją).
Do takich wyznaczników dobrego smaku należą też wcięcia. Skoro są to czemu nie uznać ich na rzeczywiste wyznaczniki bloku. Przecież każdy normalny programista ich używa. Jak dla mnie jest to genialny pomysł porównywalny z wymyśleniem rolki w myszce.
Jeśli teraz zbierzemy to wszystko razem - otrzymamy czytelny i prosty kod. Natomiast jeśli ktoś robi powyżej 3 wcięć i szeroko długie funkcje to ja dziękuję za jakość takiego kodu.
Równie dobrze można napisać, że w C++ można stworzyć takiego potwora:
costam.costaminnego.cosjeszczeinnego.jeszczecos();
W takim przypadku nawet Smalltalk wygląda ładniej.
Upośledzenie lambda jest celowym działaniem. Jeśli potrzebny jest bardziej skomplikowany fragment, to czemu nie użyć normalnej funkcji? Przecież funkcje można osadzać, istnieją domknięcia. Nie widzę żądnego problemu. Jeśli chcesz mieć to ładniej, użyj Ruby albo CLang i ich BLOCK. Co nie znaczy, że możliwość cokolwiek zmieni. Lambda ma być prosta z definicji, inaczej jest to zły kod.
Komentarze liniowe, jak i grupowe tabulacje wspomaga większość normalnych edytorów programistycznych. Nie widzę w tym problemu.
Widoczność pól to temat wałkowany od dawna. Takie rzeczy sprawdzają się i tak jedynie w językach statycznych i kompilowanych, gdzie od razu kompilator na podstawie typu stwierdzi, że do danego atrybutu jest dostęp albo go nie ma. W językach dynamicznych jest to tylko kolejny keyword wywołujący wyjątek po wywołaniu atrybutu. Więc nie widzę tu żadnej przewagi PHP. I tak trzeba testować i tak.
Dynamiczne typowanie jak i widoczność pól możemy sobie ograniczać na własne życzenie i w zakresie jakim chcemy. Co nie zmienia faktu, że stosowanie takich metod to raczej brzydka praktyka.
Dynamiczne programowanie wymaga zmiany podejścia. Zamiast ciągle powtarzać, że to złe zacznij się zastanawiać jak to można wykorzystać (chyba bez powodu te wszystkie frameworki nie zyskały popularności?). Nie ma jednej drogi idealnego programowania. Python podoba mi się z tego powodu, gdyż osławione stwierdzenie 'Programuj myśląc "co?", zamiast "jak?"' sprawdziło się u mnie w 100%.
Wstawek z "print" nawet nie komentuję. Tylko jeszcze nie pisz, że sprawdzasz wydajność za pomocą "time.time()".
Każdy większy kod trzeba zaprojektować. Dobrą zasada jest też napisać najpierw testy. Jeśli ktoś myśli, że pominie te elementy bo języki statyczne są lepsze to się grubo myli. Zawsze może zostać w sferze FLOSS, albo robić proste rzeczy na zlecenie. Beż tego naje się i tak dużo frustracji bez względu czy będzie to Perl, Python, czy Java.
Jasne... jeszcze wierzysz, że tak zawsze jest?
nawet jeżeli nie, to co MNIE to obchodzi? no powiedz, czemu JA się mam tym przejmować?
Wg mnie powinniśmy mieć możliwość kazać kompilatorowi robić tak dużo sprawdzeń jak to tylko możliwe.
ależ kompilator powinien to robić - tyle, że nie powinno to być w standardzie języka - kompilator Pythona po prostu kuleje pod tym względem, kompilatory CL są pod tym względem o niebo lepsze. chciałem tylko zwrócić uwagę, że to, o czym mówisz, nie jest problemem języków dynamicznych jako takich.
sprae, tylko czy to nie jest taka hipokryzja, bo z jednej strony wymaga się ładnego stylu i stosowanie wcięć, ale z drugiej można być bałaganiarzem jeśli chodzi o typu zmiennych.
Naprawdę wierzysz, że wszystkie Pythonowe moduły są dokładnie testowane? Sądzisz, że nie przyjdzie Ci się użerać z modułami, które tylko czasami robią to co opisane jest w dokumentacji?
Dlatego właśnie wolę mieć do czynienia ze statycznym typowaniem, bo ono filtruje przynajmniej część błędów i jak dostaję kod, z którym mam się zintegrować, to wiem, że ta metoda zwraca typ zgodny z prototypem i nie muszę do tego pisać testów, bo zrobił to za mnie kompilator.
A popularność? W językach skryptowych jest wiele konstrukcji ułatwiających pisanie. Wbudowane wsparcie dla list zmieniających rozmiar, zbiorów, słowników... Garbage collector... To wszystko zwyczajnie ułatwia pisanie kodu.
Dynamiczne typowanie oczywiście też ułatwia pisanie kodu, ale równocześnie (czego nie można powiedzieć o wcześniej wymienionych cechach) ułatwia tworzenie błędów.
Zresztą popatrz na takiego Haskella – niby jawnie tam typów nie definiujesz, ale język jest jak najbardziej statycznie typowany.
PS. Sprawdzanie wydajności? W Pythonie?
Dodek, zacznie Cię to obchodzić, kiedy przyjdzie do Ciebie szef mówiąc „To nie działa, napraw to na jutro.”
Co do Common Lispa, to czy to o czym mówisz nie działa trochę na tej samej zasadzie co w Haskellu, czyli de facto mamy statyczne typy tyle, że kompilator sam je dedukuje?
Dodek, zacznie Cię to obchodzić, kiedy przyjdzie do Ciebie szef mówiąc „To nie działa, napraw to na jutro.”
Znaczy, mam naprawiać kawałek kodu, który spierdolił ktoś z moich współpracowników? Kul, aż odechciewa mi się pracować w branży. :)
Co do Common Lispa, to czy to o czym mówisz nie działa trochę na tej samej zasadzie co w Haskellu, czyli de facto mamy statyczne typy tyle, że kompilator sam je dedukuje?
Nie, typy są w pełni dynamiczne - chociaż z drugiej strony, możesz jawnie deklarować typy, dzięki czemu przy odopowiednich deklaracji o optymalizacji kompilatory mogą np. nie wkompilować type-checkingu w tych miejscach.
Zgadowanie typu przez kompilator jest możliwe nawet bez statycznego typowania - tyle, że wtedy kompilator może mniej, wyłapie jednak np. literówki czy tak jak w dawanym przeze mnie kodzie (random 0), ale zasadniczo im więcej mu powiesz (tzn. dasz deklaracje typów), tym więcej będzie wiedział.
Hehe. Kiedyś uknułem takie zgryźliwe powiedzonko, że programiści PHP muszą ciągle pisać wszystko od nowa, bo nikomu nie ufają.
W Pythonie zazwyczaj używa się uznanych modułów. Oczywiście mogą zawierać błędy, ale jeśli aplikacja działa prawidłowo to jest OK. Np. Ostatnio używałem cluttera, w którym jest błąd animacji kanału alfa (coś ze złym rzutowaniem typu do binarnego modułu). Nie przeszkadza mi to w używaniu jego innych zalet.
W inżynierii nie ma rzeczy idealnych. Są jedynie takie, które spełniają warunki. W doktrynie jakości też nie ma mowy o czymś idealnym. Główna zależność to ilość rzeczy, których nie spodziewa się odbiorca produktu.
Mam pewną tezę. Języki statyczne kompilowane na pewno lepiej się sprawdzają w korporacyjnych zespołach programistów. Zwykle tam 80% zespołu zalicza zadania "na odwal się". Chyba dlatego taką popularnością cieszy się C# i Java (dużo poprawek na poziomie kompilacji i eliminacja przez GC pozostałych błędów).
W tak dużych zespołach testy języków dynamicznych pożarłyby dość duże koszty, tak samo testy wycieków pamięci i błędnych adresów wskaźników w natywnych binarkach.
"Znaczy, mam naprawiać kawałek kodu, który spierdolił ktoś z moich współpracowników?"
Pomijając kontekst dyskusji, to tak, oczywiście. Jeśli ktoś nie umie pracować z cudzym kodem (co m.in. oznacza też cerowanie cudzych pomyłek), to jest dupa, nie alpinista ;)
Ale wracając do tematu, o ile rozumiem że np. ścisła kontrola widoczności w jakiś sposób pomaga definiować kontrakt API, o tyle konwencja "jeśli nazwa zaczyna się od _, to nie dotykaj" jest równie skuteczna. Oczywiście zawsze znajdą się ludzie, którzy zamiast się zastanowić czy nie powinni poprawić własnego kodu zaczną się dobierać do "bebechów", ale takich typków to i mocniejsze "zabezpieczenia" nie zatrzymają (np. w javie można obejść kontrolę dostępu przez refleksję, wbić się przez aspekty, albo dołożyć swoje klasy do tego samego pakietu).
Nie przepadam za wcięciami w Pythonie, ale tylko dlatego, że ludzie mieszają różne style (spacje, tabulacje, na dodatek w różnej ilości), i czasem się potrafią różne cuda dziać. Wolałbym gdyby Python np. wymuszał 1 tabulację na każdy poziom zagnieżdżenia (zamiast "rób jak uważasz, byle było konsekwentnie w obrębie całego pliku").
Spieranie się o statyczne i dynamiczne typowanie nie ma wiele sensu, bo jak słusznie już tu zauważono problemem na ogół nie jest sam rodzaj typowania, tylko detale danej implementacji. IMO przyszłość może leżeć właśnie w wymieszaniu obydwu podejść -- albo typy statyczne z "dedukującym" kompilatorem, albo dynamiczne z możliwością deklaracji typów tam gdzie ma to sens.
Chroń nas panbuczku przed przekombinowanymi lambdami. Nie chcę siedzieć 30 minut rozgryzając czyjś techniczny onanizm. Wyrażenia lambda są jak regexpy -- szalenie przydatne, ale trzeba wiedzieć w którym momencie przestają być czytelne.
Komentarze blokowe -- nigdy mi nie brakowało niczego pod tym względem w pythonie. Jeśli chodzi o wykomentowywanie bloków kodu, to od takich rzeczy jest system kontroli wersji. A jeśli kod wymaga takiej masy dokumentacji, to albo coś z nim jest nie tak, albo potrzebuje osobnego dokumentu technicznego. Przy czym prawie na bank chodzi o to pierwsze.
Zgodzę się co do magicznego przecinka, potrafi być dość mylący na początku. Ale da się przeżyć, IMO.
PS: Jest sporo dużych projektów robiących w Pythonie bardzo ciekawe rzeczy. Wszystko zależy od koderów.
PS#2: Testy jednostkowe służą do wykazywania błędów, ale nie braku błędów :) W dobrze prowadzonych projektach Pythonowych które widziałem stosunek wierszy kodu/testów jest na ogół 1:2 (a czasem i 1:4). Zakładając, że testy są w miarę sensowne, można sobie wtedy dość śmiało poczynać z kodem (wielkie refaktoryzacje itp.) W językach statycznie typowanych proporcje kod/test nie są aż tak rozbieżne, ale sam kod jest często dużo bardziej rozwlekły.
Ot, moje trzy grosze...
Chroń nas panbuczku przed przekombinowanymi lambdami. Nie chcę siedzieć 30 minut rozgryzając czyjś techniczny onanizm.
FSVO przekombinowany, czasami chciałoby się zrobić w lambdzie jakiegoś if-a...
Dodek: Nie lubie tego wyrażenia ale
>>> b = 10
>>> a = 0 if b < 10 else 1
>>> a
1
mały /me nie będzie wchodził między ostrza potężnych szermierzy (tym bardziej że nie doprecyzowaliście tego i owego), napomknę tylko że nie wszystkim przeszkadzają duże lambdy i kilka+ poziomów wcięć, dla mnie na przykład http://code.jquery.com/jquery-1.4.1.js jest kodem czytelnym. Możliwe że to rzecz gustu (tudzież się nie znam).
Btw, coś mi się kołacze że pisanie brzydkich lambd bywało rozwiązaniem bardziej oczywistym (w przypadku nietrywialnych zagadnień).
Odgrzewany kotlet ale:
- public/protected/private? po co?
albo
- public/protected/private? po co? to rozwiązuje problem, którego nie ma.
Pisanie takich bzdur pokazuje NATYCHMIAST z jaką klasą "programistów" ma się do czynienia. No, ale jeśli ktoś zastanawia się od jakiego języka programowania zacząć naukę i wybiera 'pythona' albo podobne to wszystko jasne.