Python

Tak się składa, że ostatnio podstawowym językiem programowania, którego używam, jest Python. W związku z tym kilka przemyśleń na jego temat i na temat rozpoczynania zabawy z nim. Disclaimer: do Pythona podchodziłem już kiedyś, jeśli muszę coś napisać, to domyślnie korzystam z Perla. Niekoniecznie pięknego, często poganiającego polecenia systemowe, ale działającego. Historycznie bawiłem się Pascalem, C i oczywiście miałem kontakt z Bashem i PHP (do ostatniego oficjalnie się nie przyznaję).

Po pierwsze, istnieją czytelne reguły, np. PEP 8, którego wszyscy używają[1] czy PEP 20. Są też narzędzia, które ułatwiają zachowanie zasad dotyczących formatowania kodu, aby był zgodny z wytycznymi. Nie sposób tu nie wspomnieć o edytorze Atom, którego kiedyś włączyłem i wydał mi się straszną kobyłą. Jednak w połączeniu z pluginami działa naprawdę dobrze. Na tyle na ile mogę stwierdzić przy tak małym doświadczeniu. W każdym razie ma wszystko, do czego używałem kate. I sporo więcej. Vim nigdy mi nie podszedł na tyle, bym go używał w bardziej zaawansowany sposób. Do szybkich poprawek zawsze było albo nano, albo właśnie vim. Ale tryb tekstowy to nie jest to, co preferuję przy pracy nad dłuższym kodem.

Po drugie, dostępnych jest dużo materiałów do nauki Pythona. Także bezpłatnych. Samych książek/kursów jest kilkanaście (choćby oficjalny The Hitchhiker’s Guide to Python). Istnieją też gry, które uczą programować w Pythonie. Choćby Codecombat, którym kiedyś się bawiłem, o którym miała być notka, ale które ostatecznie mnie nie wciągnęło, a pomysł na notkę jakoś się rozmył. Mi do gustu przypadł jednak interaktywny kurs Learn Python na Androida. Jednak jeśli coś piszę i są interaktywne testy, a nie tylko czytam, to zapamiętuję więcej. Oczywiście nawet jeśli zapamiętam to do poziomu „potrafię użyć w programie” jest jeszcze kawałek, ale przynajmniej rozumiem przykłady i gotowy kod.

Po trzecie, istnieje sporo gotowych modułów, które ułatwiają pisanie. Jednak nawet gdyby nie istniały, to kod można „pożyczać” z innych skryptów przy pomocy import. I można mieć różne wersje bibliotek za sprawą środowisk wirtualnych. Z jednej strony fajne, z drugiej tworzy bałagan. Do developerki przydatne i zgodne z modną ostatnio ideą konteneryzacji. Ogólnie nauczyłem się z tym żyć, ale odruchu, żeby wszystko robić w virutalenv jeszcze nie mam.

Skoro mowa o bałaganie, to pora na wady, a tych jest zaskakująco wiele. Po pierwsze, Python 2 nadal żyje, ma się dobrze i można go spotkać na wolności. Taka umowna wada i na razie nie bardzo przeszkadza. Niemniej, nie każdy kod napisany w Pythonie 3 daje się wykorzystać w wersji drugiej.

Inna trochę dziwna na początku rzecz, to struktury danych. Jest tablica (list, w Perlu array), jest tablica asocjacyjna (dict, w Perlu hash). Ale są też tuple (niemodyfikowalne listy) i sety (nieuporządkowane zbiory unikatowych wartości). Nie żebym nie widział zalet, ale żeby aż wydzielać to? Zwł. sety (normalnie to po prostu się hasha tworzy, klucze są nieuporządkowane i unikatowe)…

To co się najbardziej rzuca w oczy: wcięcia. Są wymagane i rzutują na kod. Najprostszy przykład to:

for i in range (3):
    print ("wartosc: ")
    print (i)

Usunięcie wcięcia w ostatnim wierszu totalnie zmieni sposób działania programu. Drażni szczególnie na początku, gdy chcemy coś na szybko przetestować i np. coś wklejamy. Trzeba poprawić i dokładnie sprawdzić wcięcia, inaczej mogą być kwiatki jak wyżej.

Kolejna rzecz, która irytuje, to wciskanie filozofii w stylu zen. Na siłę i niekoniecznie zgodnie z prawdą. Weźmy mój ulubiony przykład:

There should be one– and preferably only one –obvious way to do it.

No to teraz bierzemy na tapetę set. Można go utworzyć przez

mojset = set([1, 2, 3])

Ale od wersji 2.7 można użyć do tworzenia równoważnej formy

mojset = {1, 2, 3}

Nie ma problemu? No to jak utworzyć pustego seta? Gdyby ktoś wpadł na całkiem logiczny pomysł użycia

mojset = {}

to niech wie, że nie pustego seta stworzył, a pustego dicta i za chwilę dostanie błędem po oczach. BTDT, dobry kwadrans dumania, czemu to nie działa. Dopiero wezwana pomoc uratowała sytuację, bo nie wiem ile bym jeszcze nad tym dumał.

Także one way my ass, skoro Tim Toady wita nas już na samym wstępie, przy tworzeniu jednego z podstawowych typów danych.

Możliwe, że dla ludzi, którzy zaczynają programowanie od Pythona, wady nie są tak zauważalne czy irytujące.

Niemniej, oddaję sprawiedliwość – w Pythonie pisze się przyjemnie i w praktyce jest to łatwiejsze, niż wygląda na pierwszy rzut oka. Wystarczy przejrzeć jakiś tutorial. Zdarzało mi się pisać rozszerzenia do istniejących skryptów po kilku dniach styczności z językiem i nie było większych problemów. Więc faktycznie, może być czytelnie, prosto i wygodnie. Oczywiście mogło być tak, że na ładne skrypty trafiłem. 😉

Z Perlem oczywiście nie kończę, bo do pewnych rzeczy nadaje się IMHO lepiej. Trochę gratów mam w nim napisanych, ale wersji 6 raczej już nie zacznę.

[1] A przynajmniej prawie wszyscy. A przynajmniej starają się. Prawie wszyscy.

UPDATE: Wspomniany kurs Learn Python skończyłem i szczerze polecam. Świetna podstawka, traktująca szeroko o różnych zagadnieniach, przy czym, jednak, te praktyczne części są lepsze, a teoretyczne kuleją. Jest w sumie o wszystkim najważniejszym, z programowaniem obiektowym i regexpami włącznie. Zapomniałem też wspomnieć o ipython – bardzo fajny do szybkiego sprawdzenia jakichś drobiazgów.

Swoją drogą, dziwi mnie to wciskanie regexpów do nauki programowania. To jest IMHO osobna działka zupełnie i nijak się nie klei z resztą.

Przy okazji kolejny kamyczek do ogródka w Pythonie jest inaczej. Tym razem chodzi o re.match, które działa totalnie nieintuicyjnie, choć przyznaję, że w sposób zgodny z dokumentacją. Otóż ww. funkcja dopasowuje wyrażenie tylko na początku stringa. Czyli ma takie niejawne, hardcodowane ^ – i albo .* na początku, albo korzystamy z re.search, żeby było normalnie. One way my ass, ponownie. Szczęśliwie wyszło to na Learn Python, nie w praktyce. Nie wiem co za piękny umysł to wymyślił i w imię czego…

Porządki z telefonem

Nie przepadam za smartfonami do dzwonienia. Zdecydowanie lepiej w tej roli sprawdza się wg mnie dumbphone typu Nokia 3110c (z dostępem do netu od biedy, zresztą). No ale koniec końców trzeba było po parunastu latach zmigrować – tu operatorzy powinni się zastanowić nad tworzeniem grafów powiązań userów i co oznacza naprawdę przeniesienie danego numeru do innej sieci. W każdym razie wylądowałem w Virgin Mobile. Przy tej okazji ściągawka wybranych krótkich kodów w Virgin Mobile. Z fajnych rzeczy – mają pakiety Freemium, które nieźle nadają się do nawigacji samochodowej (300MB internetu) i pozwalają bez stresu sprawdzić w praktyce zasięg, jakość rozmów itp. Znaczy przynajmniej kiedyś, nie wiem jak to wygląda teraz, jak jest obowiązek rejestracji kart SIM…

W każdym razie największym wyzwaniem okazała się synchronizacja kontaktów. Coś miałem w starej Nokii na telefonie, większość na karcie SIM, poza tym nowy telefon też zdążył dorobić się osobnej bazy kontaktów. Wpadłem na pomysł sczytania wszystkiego na komputer (od razu backup by się zrobił…), przejrzenia, posortowania i wczytania na telefon. Na szczęście nie bawiłem się w sczytywanie z Nokii 3110c, choć kiedyś to opisywałem, tylko stwierdziłem, że zrobię eksport do pliku, następnie przełożę kartę SIM i zrobię kolejny eksport. Trochę z dmuchaniem na zimne, bo jeszcze musiałem skopiować kontakty z telefonu na kartę, a pewności nie miałem, czy telefon realizuje to przez złączenie, czy nadpisanie.

Przy okazji – nie wiem kto projektował eksport kontaktów na kartę w Androidzie, ale… skopał. Znacznie prościej wysłać mailem, niż zapisywać na kartę, bo eksport następuje – jak informuje telefon – do uroczo intuicyjnej lokalizacji /mnt/sdcard/System/PIM/PIM00001.vcf. Nielinuksiarzy pewnie wystraszy już pewnie /mnt, a po przełożeniu karty do komputera można się lekko naszukać. No bo po co zrobić ten eksport w głównym katalogu karty SD… U mnie skończyło się kolejnym eksportem, żeby doczytać komunikat o lokalizacji.

Okazało się, że kontakty zarówno na Nokii, jak i Androidzie są łączone, ale… powtarzające się kontakty tworzą duplikaty. Nawet jeśli są identyczne. Bardzo user friendly. W każdym razie zerknąłem na strukturę pliku z eksportem z Androida (vCard) i stwierdziłem, że szybciej będzie mi zrobić skrypt, który usunie duplikaty, niż szukać czegoś, co się tym zajmie. I tak powstał vCardUniq.

Skrypt robi tylko jedną rzecz – czyta ze STDIN vcardy, eliminuje identyczne (muszą być obecne i zgadzać się wszystkie dane, by były identyczne) i wypluwa na STDOUT wynik, który można wczytać do telefonu. Może się komuś przyda…

Aero2 – powiadamianie o konieczności wpisania CAPTCHA

Kolejny wyjazd na urlop i znowu dostęp do internetu sponsoruje Aero2. Oczywiście w wariancie z kompresowanym tunelem i proxy cache’ującym. Pewnie gdyby nie to rozwiązanie, to po prostu kupiłbym pakiet w Aero2, bo są naprawdę tanie. A jak testowałem przy okazji awarii netu u mojego ISP – śmiga to bardzo dobrze. Tak czy inaczej, gołe Aero2 jest nieprzyzwoicie wolne do WWW. Po włączeniu ww. proxy śmiga na tyle dobrze, że stwierdziłem, że nie ma sensu kupować. Tym bardziej, że nie pamiętam, czy aktywują od razu itp., a po włączeniu rozwiązania z tunelem wszystko działało. Przy czym najwięcej czasu przy włączaniu zajęło odszukanie wpisu z opisem.

W związku z użyciem ww. proxy WWW pojawia się jeden problem: nie działa przekierowanie. Co za tym idzie, podstawowa przeglądarka nie wyświetla w takiej konfiguracji pytania o captcha. Zresztą, nawet gdyby przekierowanie działało, to mało ono pomaga przy czynnym korzystaniu z internetu, tj. nie przy biernym przeglądaniu, ale np. podczas pisania komentarza na jakiegoś bloga. Co prawda dzięki dodatkowi Lazarus do przeglądarki (polecam!; dead link) treść komentarza nie zginie nawet jeśli funkcja cofnij w przeglądarce nie działa i nastąpi przekierowanie. Postanowiłem jednak sprawę ułatwić, przez napisanie prostego skryptu w Perlu, który sprawdzi dostępność zdefiniowanych hostów. A przy braku dostępności zadanej ilości wyświetli wyskakujące powiadomienie (xmessage). Przy czym sprawdza dość często (co 15 sekund), a jak brak łączności, to przerwa jest dłuższa.

Wyszło trochę ciekawiej, niż się spodziewałem. Wiadomo, że mocną stroną Perla są moduły, więc postanowiłem oprzeć się na dedykowanym module. Po pierwsze, okazało się, że Net::Ping domyślnie korzysta z TCP, nie ICMP, którego planowałem użyć, ale wydało się nawet lepsze. Niestety tylko do czasu, gdy okazało się, że przy podaniu hostów w formie domen i sprawdzaniu portu 80, za sprawą mechanizmu przekierowania Aero2, strona nadal jest dostępna. Tyle, że jest to strona mówiąca o konieczności wpisania captcha.

Rozwiązania są trzy. Pierwsze, to sprawdzanie zawartości strony, tj. czy nie pojawił się tekst mówiący o konieczności wpisania kodu captcha. Po pierwsze komplikuje to budowę skryptu. Po drugie, zmniejsza jego uniwersalność (będzie działał tylko dla Aero2), po trzecie, możliwe, że przestanie działać przy jakiejś zmianie komunikatu. Drugie rozwiązanie, to podanie IP zamiast nazw domenowych. Przy takim rozwiązaniu przekierowanie dokonywane przez Aero2 nie będzie miało wpływu. Mniej czytelne i… zwyczajnie nie wpadłem w pierwszej chwili.

Zamiast tego postanowiłem skorzystać z wersji najoczywistszej i pierwotnie zamierzonej: wykorzystać jednak ICMP do sprawdzania osiągalności hostów. I tu największa niespodzianka: Net::Ping (ogólnie Perl) do wykorzystania ICMP wymaga uprawnień roota. Zdziwiłem się, ale po prostu tak jest i nie jest to feature Perla, a systemu. Zwykli użytkownicy nie mają dostępu do tworzenia socketów. Z tego co znalazłem w sieci, polecane/najprostsze rozwiązanie na obejście tego to… wykorzystanie systemowego polecenia ping, do którego zwykle użytkownicy mają dostęp (SUID). Tak też uczyniłem.

Efekty można zobaczyć na mojej ulubionej sieci społecznościowej (hrhrhr), czyli w repo aero2-watch na GitHubie. Grupa potencjalnych użytkowników bardzo wąska (Linux + Aero2), ale może się komuś przyda…

Gdyby ktoś się zastanawiał, czemu klepię skrypty/siedzę na necie na urlopie to: lubię i zawsze to robię. Poza tym, do porannej kawy jak znalazł; robię też inne, typowo urlopowe rzeczy; napisanie ~50 linii w Perlu trwa znacznie krócej, niż napisanie tego wpisu.