Co wkurza w Pythonie?

Co wkurza w Pythonie?

Python jest świetnym językiem ogólnego przeznaczenia, który plasuje się na wysokich pozycjach w każdym rakingu popularności. Mnóstwo aplikacji webowych największych gigantów technologicznych korzysta z Django, większość analityków danych czerpie pełnymi garściami z funkcji tego języka. Niestety, nie ma języków idealnych. Oto rzeczy, które denerwują programistów Pythona.

Celowo unikam tutaj słowa „wada”. Mówi się, że jeśli coś jest do wszystkiego to jest do niczego. To samo dotyczy języków programowania. Elementy języka, które opiszę, nie są mankamentem ani przeoczonym bugiem, lecz wynikają z projektu języka i jego przeznaczenia. Są to kompromisy, na które przystali twórcy, aby uzyskać pożądany efekt, i należy o tym pamiętać ;)

Python jest językiem napędzanym przez społeczność, dodatkowe biblioteki i narzędzia. Krzywdzące byłoby jedynie wygarnięcie wad nie wspominając o rozwiązaniach, które te wady niwelują. Pod każdą wymienioną cechą postaram się podsunąć Wam rozwiązanie tego problemu ;)

Brak statycznego typowania

Czym jest statyczne typowanie i czym różni się od dynamicznego? Statyczne typowanie (static typing) polega na określeniu typu zmiennych podczas procesu kompilacji. W tym przypadku po określeniu typu zmiennej nie można go zmieniać.

Dynamiczne typowanie (dynamic typing) polega na przypisaniu typu zmiennej podczas wykonywania kodu, a nie na etapie kompilacji. Języki z typowaniem dynamicznym do zadeklarowania zmiennej potrzebują jedynie jej nazwy. Zmiana typu zadeklarowanej zmiennej jest możliwa.

Rozpoznawanie typu obiektu w językach z dynamicznym nazywa się duck typing i polega na badaniu metod udostępnionych przez obiekt. Nazwa wzięła się od tak zwanego duck testu: „If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck„.

Dynamiczne typowanie ułatwia proces nauki programowania, jednak w przypadku tworzenia zaawnsowanych aplikacji staje się poważnym problemem. Przez brak statycznego typowania kod staje się trudniejszy do utrzymania, może to również prowadzić do błędów, które łatwo przeoczyć. W przypadku statycznego typowania zmiana typu zmiennej poinformuje nas o problemie już na etapie kompilacji, w przypadku Pythona żaden błąd nie zostanie zgłoszony, jednak aplikacja może przestać działać prawidłowo.

Dlaczego twórcy języka zdecydowali się na właśnie takie rozwiązanie? Python był tworzony jako język z prostą składnią, która pozwoli ludziom nietechnicznym łatwiej tworzyć kod, a brak konieczności deklarowania typu zmiennej zdecydowanie to ułatwia.
Rozwiązaniem problemu może być type hinting dostępny od wersji 3.5 (we wcześniejszych wersjach Pythona potrzeba do tego dodatkowego narzędzia – mypy). Pozwala to kontrolować typy zmiennych i zwiększa czytelność kodu.

„Python jest za wolny”

Nie da się ukryć, że Python jest dużo wolniejszy od C, C++ czy Javy. Nie zawsze jednak szybkość jest na tyle istotna. Nie mówię tutaj o programowaniu silników graficznych gier, w przypadku których każda oszczędność 1ms jest okazją do świętowania ;) 15 sekund różnicy w procesie tworzenia modelu uczenia maszynowego nie ma wielkiego znaczenia, a końcowy klient naszej aplikacji webowej nie doceni strony  ładującej się 20ms krócej. Trzeba pamiętać, że języki ogólnego przeznaczenia są takie tylko z nazwy, a Python zwyczajnie nie nadaje się do obsługi systemów czasu rzeczywistego czy do gamedevu.

W niektórych przypadkach istnieje jednak możliwości przyspieszenia naszych programów napisanych w Pythonie. Pomocne w tym będą biblioteki Cython i Numba. Aby zoptymalizować kod operujący na liczbach i wykonujący wiele operacji matematycznych należy tworzyć go przy pomocy biblioteki numpy, która przygotowana pod kątem optymalnej obsługi działań matematycznych. Jeżeli nasz program korzysta z pętli z dużą liczbą iteracji warto pomyśleć na skorzystaniu z dystrybucji PyPy, gdyż w takich przypadkach może ona pozytywnie wpłynąć na szybkość wykonywania skryptów.

Piekło zależności

Na piewrszy rzut oka instalacja pythonowych bibliotek wydaje się być rozwiązaniem idealnym. Bardzo często rzeczywiście tak jest. Za pomocą polecenia pip install jesteśmy w stanie zainstalować mnóstwo przydatnych rozszerzeń. Niestety, korzystanie wyłącznie z PIP’a nie zawsze wystarczy.
Wiele bibliotek podczas instalacji wymaga konkretnej wersji innej biblioteki. Zdarza się, że podczas instalacji pakietu X pakiet Y zostanie przeinstalowany do wersji wydanej wcześniej, która z kolei koliduje z pakietem Z.
Biblioteki instalują potrzebne pakiety same? Źle! Nie robią tego? Jeszcze gorzej! Niestety taki scenariusz również się zdarza. Gdy już przeszliśmy przez etap instalacji i chcemy zacząć korzystać z biblioteki, próbujemy ją zaimportować i widzimy błąd „ModuleNotFoundError”, gdyż dokumentacja biblioteki nie poinformowała nas o konieczności posiadania innego zewnętrznego pakietu.
Sprawa komplikuje się jeszcze bardziej gdy korzystamy z Windowsa. Programiści korzystający z systemu firmy Microsoft czytając dokumentacje bibliotek i frameworków czasami spotykają się z informacją o niekompatybilności z ich systemem. „Windowsowców” przytłoczyć może również sposób instalacji innych bibliotek. W przypadku systemów UNIXowych polega ona na wykonaniu jednego polecenia w bashu, jednak na systemach z rodziny Windows nierzadko procedura składa się z kilku czasochłonnych kroków, w których szansa na wystąpienia jakiegoś błędu jest całkiem spora ;)

Rozwiązanie: środowiska wirtualne. W tym przypadku jest to nie tylko rozwiązanie drobnego problemu, lecz narzędzie, z którego korzystać powinien każdy programista Pythona. Do obsłui środowisk wirtualnych posłużą nam na przykład virtualenv + virtualenvwrapper. Ja korzystam z narzędzia Conda, które pozwala również na instalację bibliotek do innych języków.
Dobrym zwyczajem jest tworzenie środowiska wirtualnego dla każdego projektu, w którym zainstalowane będą jedynie pakiety wykorzystywane w projekcie. W przypadku Condy możemy również zainstalować różne wersje Pythona dla różnych środowisk.

Global Interpreter Lock

Global Interpreter Lock (GIL) jest blokadą, która dopuszcza działanie interpretera tylko na jednym wątku. Dlaczego w ogóle coś takiego wprowadzono? Python został wydany na początku lat 90 gdy systemy operacyjne były jednowątkowe. Zastosowanie prostego w implementacji mechanizmu GIL ułatwiło pracę programistom oraz zwiększyło szybkość wykonywania programów. Wdrożenie GIL’a było stosunkowo proste, jednak pozbycie się go na tym etapie rozwoju języka jest już bardziej skomplikowane i prawdopodobnie nigdy się to nie stanie. Trochę więcej o GIL możecie przeczytać w tym artykule od RealPython.
Co możemy wykorzystać zamiast wielowątkowości? Wieloprocesowość! Python posiada wbudowany moduł multiprocessing, który pozwala pisać wieloprocesowe programy. Nie jest to jednak rozwiązanie tak skuteczne jak wielowątkowość w innych językach. Wykorzystując 2 procesy nigdy nie uzyskamy 2 razy lepszych rezultatów czasowych.
Inną możliwością do rozważenia jest asynchroniczność. Jest to forma programowania równoległego, która pozwala na wykonywanie kodu w oddzieleniu od głównego wątku. Do tworzenia asynchronicznych programów służy biblioteka asyncio.

Niewielkie możliwości mobile developmentu

Python zdecydowanie nie jest językiem stworzonym do aplikacji mobilnych. Społeczność jednak i w tym przypadku nie rozłożyła rąk i fani Pythona mogą również tworzyć mobilne oprogramowanie w swoim ulubionym języku. Jedną z opcji jest biblioteka Kivy, która pozwala na tworzenie międzyplatformowych aplikacji. Największą jej wadą jest jednak brak „natywności”. Oznacza to, że aplikacja na Androida napisana w Kivy nie będzie wyglądała jak inne androidowe apki, gdyż korzysta ona z własnych komponentów, a nie tych przygotowanych dla danej platformy. Obiecującą poprawą w tej kwestii wydaje się być biblioteka BeeWare, która pozwala na budowanie aplikacji natywnych. Projekt ten nie jest jeszcze całkowicie gotowy, jednak na pewno warto zwrócić na niego uwagę ;)

2 komentarze

  1. Hej Kamil,
    śledzę Twoje posty od dłuższego czasu, odkąd pojawiły się w zestawieniach #unknownews.

    Jako że siedzę w tematyce Data Science, to siłą rzeczy tematy z zakresu Pythona i baz danych przyciągają moją uwagę, a Twój blog jest świetnym streszczeniem niektórych koncepcji. Przykładowo z tego posta zapisałem sobie w Pythonowym arsenale około 7 nowych bibliotek. :)

    Chciałem tylko powiedzieć, że robisz świetną robotę!
    Trzymaj tak dalej!

    1. Cześć Paweł!
      Bardzo Ci dziękuję za te miłe słowa ;) Cieszę się, że moje posty mogą komuś chociaż trochę pomóc ;)
      Pozdrawiam! ;)

Leave a Reply

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *