Jak połączyć Node'a z Pythonem

Dzisiaj będzie krótko i zwięźle. Grunt to żeby zostawić trochę wiedzy w cyberprzestrzenii.

Jeśli zamarzy wam się kiedyś uruchomić zewnętrzny proces wewnątrz Node.js i czytać z niego to pewnie po szybkim rozeznaniu skończycie mniej więcej z takim kodem:

var exec = require('child_process').exec; 
var child = exec('python sample.py');
child.stdout.on('data', function(data) {
console.log(data); }); child.stderr.on('data', function(data) {
console.log(data); });

Jeśli to będzie proces działający długotrwale to czeka was spore rozczarowanie. Żadne dane nie spłyną do standardowego wyjścia dopóki się nie zakończy. Co ciekawe to dotyczy tylko Pythona. Pozostałe programy nie mają z tym problemu. Oczywiście skoro problem jest specyficzny dla Pythona to tam też jest rozwiązanie. Wystarczy użyć dodatkowej opcji:

python -u sample.py

Nie rozgryzłem dlaczego np. terminal nie ma z tym problemu, ale dzisiaj nie mam już do tego głowy.

Projektowo

Doprowadziłem dzisiaj Parowca do działania (po dość długiej przerwie). Dalej nie wiem dlaczego zdychał, w każdym razie teraz pilnuje go Supervisord, więc jak mu się padnie to powinien zaraz wstać.

Poza tym pracuję nad certyfikatem SSL. W końcu HTTPS jest teraz w modzie. W ciągu paru następnych dni planuję spłodzić jakąś poważniejszą notkę (to znaczy coś konkretnie o programowaniu, a nie jakieś wpisy, że zmieniłem kolory na blogu). No i startuję już jakiś poważniejszy projekt.

Projekty
Poważniejszy niż przynajmniej 90 innych projektów w tym katalogu...

To chyba na tyle. Czas spać (i czytać) :)

Python vs Scala

Postanowiłem dzisiaj porównać ulubiony (wahałem się użyć tego słowa, bo to jednak tylko narzędzie) język programowania czyli Pythona z ciekawie się zapowiadającym, jeszcze nieodkrytym przez mnie do końca językiem Scala. Postaram się opisać je w miarę bezstronnie. To nie będzie trudne, bo jestem równie entuzjastycznie nastawiony do obydwu :) Też w żaden sposób nie widzę w nich konkurencji. Na razie parę faktów z wikipedii o obu językach.

Python

Powstał w 1991 roku, jest językiem obiektowym i proceduralnym z silnym, dynamicznym typowaniem i elementami funkcyjnymi. Ostatnia wielka zmiana nastąpiła w roku 2008, kiedy wydano Pythona 3. Główną implementacją jest CPython, który jest interpreterem w języku C, ale z biegiem lat powstało też wiele innych. Zasady jakie mu przyświecają to czytelność, prostota i zwięzłość. Głównie używany wśród naukowców i ekosystemie Linuksa jako prostsza alternatywa dla C/C++ w aplikacjach desktopowych. Bardzo trafnie kiedyś określony jako "kompilowalny pseudokod".

Scala

Język stworzony przez ludzi, którym przeszkadzała różne braki w Javie, pierwsza wersja powstała w 2003. Ostatnia większa zmiana w 2006 roku (wersja 2.0). Całkowicie obiektowy z silnym, statycznym typowaniem i również z elementami funkcyjnymi, ponadto zawiera mechanizmy wspierające obliczenia współbieżne. Kod źródłowy można skompilować pod maszynę JVM lub .NET. Stawia na prostotę, skalowalność i funkcyjność. Używany głównie przez twórców i znudzonych javowców :) Nazywany "lepszą Javą".

Typowanie

Fundamentalna sprawa. Wiele w internecie można znaleźć dyskusji na temat dynamicznego vs statycznego typowania. Można z nich (podobnie jak z prawie wszystkich dyskusji typu jabłka vs gruszki) wysnuć jeden wniosek: to zależy. Dynamiczne typowanie upraszcza wiele rzeczy, daję większą swobodę, dopuszcza monkey-patching (modyfikowanie instancji obiektów w trakcie działania programu). Statyczne typowanie zwiększa czytelność i daje większe bezpieczeństwo. Dlatego nie zamierzam porównywać co jest lepsze, bo to trzeba dopasować do sytuacji. Dlatego też osobiście mam zamiar używać obu języków.

Benchmarki

Jeśli chodzi o wydajność oparłem się o wyniki The Computer Language Benchmarks Game. Wynika z nich, że Scala jest sporo szybsza. Python za to zajmuje mniej kodu i ma mniejsze zapotrzebowanie na pamięć. Nie jest to niespodzianką, wiadomo, że JVM generuje szybki kod i zżera bardzo dużo pamięci. Standardowy Python to tylko interpreter, więc nie można było liczyć na wiele. Niestety nie ma wyników dla PyPy. Postaram się w wolnej chwili przygotować własne. Zrobiłem tylko kilka na szybko, w których Scala miała co najwyżej trzykrotną przewagę nad PyPy i 50-krotną nad standardowym Pythonem.

Moduły

W Pythonie moduły z poziomu języka widoczne są jako obiekty. Takie obiekty mogą zawierać w sobie funkcje, klasy, a także luźny kod, który jest wykonywany w momencie importu. Co ważne import można wykonać w niemal dowolnym bloku kodu. W Scali wygląda to bardzo podobnie, ale oczywiście jest całkowicie obiektowa i nie można w module umieścić luźnych funkcji i kodu. Poza tym modułem jest katalog (wszystkie klasy, interfejsy itp. w plikach źródłowych po skompilowaniu "wyskakują" jako osobne pliki tak samo jak w Javie), a nie plik z modułem jak w Pythonie.

Obiektowość

W jednym i drugim języku wszystko jest obiektem, jednak Scala wykazuje się tu większą spójnością. W Pythonie metody wywołuję się przez konstrukcję obiekt.metoda(), a operator np. dodawania a + b wywołuje a.__add__(b). W Scali są dwie możliwości wywoływania metod: a.metoda() (() opcjonalne) i a metoda. Ta druga konwencja moim zdaniem zmniejsza czytelność w przypadku zwykłego kodu, ale bardzo elegancko rozwiązuje sprawę operatorów. Mianowicie a + b to tak naprawdę a.+(b). Pozwala to tworzyć własne operatory, podczas gdy Python ogranicza nas tylko do kilku wbudowanych. Warto wspomnieć, że oba języki mają akcesory i mutatory (gettery i settery, ale trzeba dbać o czystość języka polskiego :P) deklaruje się dużo bardziej elegancko niż w Javie. Jeśli stwierdzimy, że jakieś pole oprócz przy przypisywaniu lub odczycie ma robić coś więcej to w Pythonie używamy dekoratorów (adnotacje dla javowców, "małpki" dla pozostałych), a w Scali definiujemy odpowiednie metody (zadanie domowe: wymyśl jak to zrobić, cała wiedza potrzebna do tego jest w tym akapicie... (Scala pozwala zastępować znak "_" w nazwie metody na spację)... teraz już jest :)).

Scala też ciekawie radzi sobie z metodami statycznymi. Klasy nie mogą zawierać statycznych metod i pól. Po to powstał taki twór jak obiekty (object). Istnieje tylko jedna instancja obiektu, nazwy mogą się pokrywać z nazwami klas, przez co jeśli chcemy stworzyć klasę z "normalnymi" metodami i statycznymi musimy rozbić to na klasę i obiekt o tych samych nazwach. Szczerze mówiąc nie widzę zbyt wielu zalet takiego podejścia. W Pythonie gdy chcemy stworzyć jakiegoś helpera wyładowanego statycznymi metodami to po prostu robimy moduł z funkcjami. Z zewnątrz wygląda to identycznie jak w Scali, czyli jedna instancja obiektu z metodami. Jeśli chcemy w Pythonie klasę mieszaną to metody statyczne oznaczamy odpowiednimi dekoratorami (podobnie jak w innych językach, tylko zazwyczaj to są słowa kluczowe).

Jeśli chodzi o składnię to Scala z jednej strony daje możliwość pisania podobnie jak w Pythonie (bloki oparte o wcięcia, jednolinijkowe "czyste" funkcje, bez średników), a z drugiej pozwala pisać podobnie jak w Javie (bloki oparte o klamry i średniki).

Polimorfizm w Pythonie to po prostu zwykłe wielokrotne dziedziczenie, natomiast w Scali są znane z Javy pojedyńcze dziedziczenie i interfejsy, a ponadto cechy (trait, ale tak to można chyba tłumaczyć) czyli struktury z zaimplementowanymi metodami, które można doczepiać potem do klas.

Hermetyzacja w Scali jest właściwie identyczna jak w Javie, natomiast w Pythonie można utrudnić dostęp do niektórych atrybutów obiektu poprzez dodanie "_" lub "__" w nazwie, ale jeśli się chce to do wszystkiego można się dostać.

Zmienne

W Pythonie nie deklarujemy zmiennych, po prostu przypisujemy jakąś wartość, a interpreter w razie potrzeby utworzy zmienną o podanej nazwie. W przypadku odwoływania się do zmiennych globalnych trzeba użyć słowa kluczowego global, ale używanie zmiennych globalnych to zazwyczaj nie jest dobry pomysł :) W Scali zmienne deklarujemy przez var nazwa[:typ], a stałe (oczywiście tylko referencja jest stała, obiekt już niekoniecznie) przez val nazwa[:typ]. Jak widać typ jest opcjonalny, o ile kompilator jest w stanie sam się domyśleć jaki typ jest nam potrzebny.

Biblioteka standardowa

Tu też Scala jest bardziej spójna niż Python. Mimo że wersja 3.0 wprowadziła trochę porządku to i tak są pewne rozbieżności jeśli chodzi o nazewnictwo i styl. Scala jako, że ma działać zarówno na JVM jak i na .NET, wprowadza swoje nakładki na klasy w poszczególnych środowiskach. Dzięki temu jeden kod może działać na obu maszynach (oczywiście tylko ten bez zbędnych luksusów jak baza danych albo GUI :)). Co najważniejsze Scalowe wersje standardowych obiektów zawierają wiele nowych metod (głównie z dziedziny programowania funkcyjnego) w porównaniu z natywnymi odpowiednikami. W Pythonie od zawsze denerwowała mnie taka na wpół obiektowa składnia w najbardziej podstawowych obiektach. Z jednej strony możemy wywołać "a b c".split(), ale żeby sprawdzić długość trzeba wywoływać funkcję len("cośtam"), która wywołuje metodę "cośtam".__len__(). Nie wiem czemu w wersji 3.0 się tego nie pozbyli. Scala za to wprowadziła pewną rzecz, która wcześniej bardzo mi się podobała w Haskellu. Na wstępie ładuje obiekt scala.Predef i jeśli wywołamy "funkcję" np. println to tak naprawdę wywołujemy odpowiednią metodą z obiektu Predef.

Programowanie funkcyjne

W jednym i drugim wygląda to bardzo dobrze. Różnica jest taka, że Python zgodnie ze swoją filozofią trzyma funkcje związane z programowaniem funkcyjnym (tak, chciałem napisać funkcje funkcyjne :)) w module functools, a w Scali są to oczywiście metody w odpowiednich obiektach (np. kolekcjach). Oczywiście oba języki mają funkcje lambda wbudowane w składnię, a Python nawet całkiem eleganckie definiowanie zbiorów.

Refleksja

W tym punkcie nie ma niespodzianki, Python po prostu miażdży Scalę. Możemy zmieniać instancje i deklaracje klas w locie, a nawet tworzyć metaklasy i przeładowywać moduły. Do tego każdy obiekt zawiera metody specjalne, który możemy nadpisywać dla osiągnięcia ciekawych efektów, np. tworzyć w locie metody na podstawie nazwy (__getattribute__). Scala pozwala nam właściwie tylko na tyle, na ile pozwala JVM. Jest o tyle lepiej, że funkcje są obiektami, ale tylko dlatego, że kompilator je sprytnie opakowuje w klasy. Oczywiście dynamicznie załadować albo stworzyć klasę możemy, ale nadpisać już załadowaną tylko w trybie debugowania.

Dokumentacja

Jeśli miałbym pokazać największe wady Pythona, to na pewno na pierwszy miejscu wyląduje dokumentacja. Jest straszna. Tak jak bardzo lubię ten język, tak używanie dokumentacji jest zazwyczaj koszmarem. Brak jakiegoś sensownego uporządkowania, zawsze mam problemy z poruszaniem w wygenerowanych PyDocach. Żeby nie było to czepiam się tylko wersji HTML, w trybie interaktywnym dokumentacja jest bardzo pomocna (wywołuje się ją przy pomocy funkcji help(obiekt) lub help(obiekt.metoda)).

ScalaDoc (bo tak oczywiście się ten twór nazywa) to po prostu ulepszony JavaDoc. A ponieważ JavaDoc jest świetny, więc ScalaDoc jest nawet trochę lepszy. Najbardziej podoba mi się, że z każdej strony można przejść do odpowiadającego kodu źródłowego. Bardzo przydatne jest też filtrowanie i wyszukiwanie. JavaDoc powstał w czasach, gdy JavaScript nie był tak mocny jak teraz więc nikomu nie przyszło wtedy dodanie takich funkcji.

Paczkowanie

W tym aspekcie między obydwoma językami jest duża różnica... filozoficzna. Python jak wcześniej pisałem jest mocno związany ze środowiskiem Linuksa, tak że moduły powinny leżeć w systemie i być instalowane z repozytorium dystrybucji, ewentualnie z repozytorium Pythona. Oczywiście jeśli jest taka potrzeba można instalować moduły per użytkownik albo nawet tworzyć izolowane środowiska z virtualenv. Jeśli chodzi np. o użytkowników Windowsa to można wszystkie moduły i interpreter spakować do jednego execa i wtedy użytkownik nie musi się przejmować zależnościami.

Scala wywodzi się z Javy, która wszystkie zależności trzyma w projekcie. Tutaj jest Maven i inne tego typu narzędzia. To już kwestia gustu, które podejście nam bardziej odpowiada.

Współbieżność

Nie czuję się kompetentny w temacie, nie tworzyłem nigdy projektu, który by miał więcej niż kilka wątków :) Scala była projektowana pod programowanie współbieżne, więc ma takie bajerki jak aktorów i kolekcje współbieżne, pewnie na tym polu wygrywa.

Podsumowanie

Jak pisałem na wstępie, nie zamierzam oceniać, który jest lepszy. Wedle mnie obydwa języki są świetne w różnych tematach. Bardzo bym też chciał, żeby twórcy Pythona podpatrzyli parę rzeczy od Scali i umieścili kiedyś w Pythonie 4.0 :) W Scali też nie jest wszystko idealne, ale widać, przez to, że język był projektowany na początku wieku i twórcy mogli czerpać z wieloletnich doświadczeń twórców innych języków, jest znacznie bardziej przemyślany niż większość tych starszych. Dużą zaletą jest też to, że można projekty napisane w Scali niemal bezboleśnie podczepić pod istniejące projekty Javy i odwrotnie. Za to Python daje ogromną łatwość pisania kodu i zwięzłość. Mam nadzieje, że z pomocą moich, umieszczonych tutaj obserwacji każdy sam sobie wyrobi opinię o tych językach.

A, i jeszcze jedno. Oba języki są bardzo rozbudowane i temat nie był łatwy do opracowania. Mimo, że starałem się wybadać wszystkie rzeczy, które tutaj pisałem, mogłem o czymś nie wiedzieć/zapomnieć/nie zauważyć. Wszelkie znalezione błędy oczywiście proszę wytykać w komentarzach :)