Wstęp do pythona

Z RoboCity
Wersja z dnia 01:04, 6 lip 2016 autorstwa Isj (dyskusja | edycje) (Jak zacząć programować?)

Kilka słów o języku programowania python. Skupimy się na używaniu tego języka pozostawiając szczegóły techniczne na boku.

Jak w ogóle działa język programowania i co to jest?

Komputery są przydatne o tyle, o ile potrafią wykonać to, czego od nich chcemy.

Jednym ze sposobów przekazywania innym, w miarę rozgarniętym bytom, czego od nich oczekujemy są instrukcje. Możemy na przykład poinstruować rodzeństwo, żeby nie dotykało tej kupki części i... Przynajmniej mieć nadzieję, że instrukcje zostaną wykonane. Jednak pomijając kłopoty komunikacyjne zastanówmy się chwilę co właściwie zrobiliśmy. Po pierwsze użyliśmy znanego pewnie nie więcej niż 100mln ludzi specyficznego języka (polskiego) z jego wszystkimi zasadami wymowy czy gramatyki, żeby przy pomocy dość skomplikowanej aparatury (nasze gardło i język -- powietrze -- uszy odbiorcy) przekazać myśl opisującą jakiego zachowania oczekujemy.

Dokładnie na tym samym polega programowanie komputerów. W tej chwili jeszcze nie jest możliwe zagajenie rozmowy z komputerem w języku naturalnym, który jest zbyt zawiły i za bardzo wieloznaczny. Tym bardziej nie było to możliwe wcześniej. Z tego powodu informatycy opracowali pewne prostsze i bardziej precyzyjne języki. Ponadto zrezygnowali z rozmów z komputerami i przyjęli zasadę, że treść programu trzeba dostarczyć na piśmie, co w przypadku komputerów oznacza plik tekstowy lub tekst wprowadzony z klawiatury.

Jeśli nauczymy się zapisywać swoje intencje, w którymś z języków, które nasz komputer rozumie, będziemy mogli wykorzystywać go do różnych, mniej lub bardziej przydatnych zajęć. Jednym programistom podoba się "przekonywanie" komputerów do wyświetlania wirtualnych światów, w których inni ludzie lubią przebywać. Innym udaje się tworzyć zestawy instrukcji, dzięki którym komputer potrafi samodzielnie kierować samochodem. Kody napisane przez niektórych sterują sondami w przestrzeni kosmicznej, które muszą samodzielnie zbierać dane (są zbyt daleko żeby człowiek mógł reagować na bieżąco!) i posyłać wyniki czekającym na Ziemi ludziom. Jak widać na filmach poniżej są też tacy, którzy tworzą zestawy instrukcji do tańca na śniegu albo sprytnych piesków.

Kiedy program steruje jakimś większym urządzeniem efekty błędów w kodzie bywają zaskakujące.


Okazuje się więc, że pomimo prostoty języków programowania, potrafimy z ich pomocą wyrażać naprawdę skomplikowane instrukcje oraz zupełnie nieźle się przy tym bawić!


Warto zwrócić uwagę na fakt, że w odróżnieniu od rodzeństwa, maszyny ciągle jeszcze są dość bezmyślne i z chęcią zrealizują dowolne instrukcje o ile tylko zapiszemy je w zrozumiały dla nich sposób. Niestety ta zaleta komputerów jest jednocześnie ich najgorszą wadą. Kiedy instrukcje są bez sensu, maszyny wykonają działanie bez sensu bez sprzeciwu. Niestety z tego powodu programowanie przypomina opiekę nad bardzo wydajnym, ale jednak ciągle "osiołkiem".

Praktyczna zasada: Jeżeli "komputer robi coś głupiego", to najwyższa pora przemyśleć instrukcje, które otrzymał; już dłużej nie da się zwlekać.


Jak zacząć programować?

Wystarczy włączyć terminal i wpisać polecenie python.

ktos@maszyna:~$ python
Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

W odpowiedzi zostaje uruchomiony program, który czeka na nasze instrukcje i będzie je wykonywał w miarę wpisywania. Stan oczekiwania na instrukcje sygnalizowany jest wyświetleniem >>> na początku linii. Na samym początku python wyświetla też kilka dodatkowych technicznych informacji na temat języka, oraz zachętę do skorzystania z kilku instrukcji w celu poznania dalszych szczegółów. Możemy te linijki zignorować.

Kiedy już skończymy wykonywanie różnych instrukcji pythona możemy zakończyć jego działanie i wrócić do linii poleceń terminala używając kombinacji klawiszy Ctrl-D. Możesz to sprawdzić od razu, ale nie zapomnij ponownie uruchomić pythona przed przejściem do następnych przykładów.

Kilka prostych instrukcji i konstrukcji języka python na rozgrzewkę

Dla wygody poniżej posługujemy się kolorowaniem składni. W terminalu nie będzie ono widoczne, ale dzięki niemu poniższe przykłady czyta się łatwiej.

print (ang. drukuj), czyli wyświetlanie napisów

Spróbujmy wykonać jedną z prostszych instrukcji, która po prostu wypisuje podany tekst. Kiedyś, kiedy będziemy pisali dłuższe programy, taka instrukcja przyda nam się na przykład do wyświetlenia wyniku, albo komunikatu, że program skończył działanie pomyślnie i wykonał to czego chcieliśmy.

>>> print("Nie będę śpiewał kołysanek na lekcji")
Nie będę śpiewał kołysanek na lekcji
>>>

Jak widać programowanie w pythonie jest zupełnie proste. Napisaliśmy nazwę funkcji (print) po czym w nawiasie podaliśmy parametr (tutaj tekst, który należy wydrukować) i tyle. Po naciśnięciu ENTER zobaczymy nasz tekst.

Zwróćmy też uwagę, że tekst otoczony jest znakami cudzysłowu. Chodzi o to, że gdybyśmy go napisali bez cudzysłowu, na przykład print( print print ), mogłoby być trudno w niektórych sytuacjach zrozumieć, które słowa należy traktować jak zwykły kawałek tekstu do wyświetlenia, a które należy interpretować jako instrukcje programu. Kiedy jednak napiszemy print( "print print" ) będzie oczywiste, że należy wyświetlić napis print print.

Jak widać powyżej tekst został wyświetlony w pierwszej pustej linii na ekranie, a zaraz pod nim ponownie znak oczekiwania na kolejną instrukcję. Wszystko razem wygląda trochę ciasno. Żeby temu zaradzić dodajmy zarówno przed tekstem jak i po nim znak przejścia do nowej linii. Przejście do nowej linii zostawi pustą linijkę dzięki czemu wyświetlony komunikat będzie się lepiej wyróżniał.

 >>> print("\nNie będę śpiewał kołysanek na lekcji\n")
 
 Nie będę śpiewał kołysanek na lekcji
 
 >>>

A co jeśli chcielibyśmy wyświetlony tekst powtórzyć? Python pozwala użyć mnożenia w tym celu.

>>> print("\nNie będę śpiewał kołysanek na lekcji"*5)

Nie będę śpiewał kołysanek na lekcji
Nie będę śpiewał kołysanek na lekcji
Nie będę śpiewał kołysanek na lekcji
Nie będę śpiewał kołysanek na lekcji
Nie będę śpiewał kołysanek na lekcji
>>>

Wyświetlanie napisu wielokrotnie ma raczej mały sens ale, choć dla człowieka takie zadanie byłoby karą, komputer wykonuje kod bez problemu. Jeśli pomyłkowo pomnożymy napis milion razy, funkcja print cierpliwie wyświetli milion napisów...............

Na wszelki wypadek dodajmy przy okazji, że zbyt długo pracujący program można przerwać kombinacją klawiszy Ctrl-C.

Czasem jednak powielenie tekstu może być przydatne. Na przykład przepis na wyświetlenie choinki mógłby wyglądać tak (zwróć uwagę, że jeśli w jednej linii podajemy wiele instrukcji -- tutaj print -- to rozdzielamy je średnikiem ;):

>>> print(" "*10 + "^"*1); print(" "*9 + "^"*3); print(" "*8 + "^"*5);print(" "*7 + "^"*7);print(" "*6 + "^"*9);
          ^
         ^^^
        ^^^^^
       ^^^^^^^
      ^^^^^^^^^
>>>

Dzięki temu, że możemy łatwo powielić tekst poprzez mnożenie, stworzenie choinki składającej się z coraz krótszych kawałków spacji (10, 9, 8, 7, 6) oraz coraz dłuższych pięter choinki (znaki ^ powtórzone 1, 3, 5, 7 i 9 razy) było proste w zapisie. Jeśli nie wierzysz, spróbuj za pierwszym razem uzyskać ładną symetryczną choinkę licząc znaki poszczególnych pięter samemu i wstawiając odpowiednio długie ciągi znaków w kolejnych instrukcjach print.

Drukując napisy możemy jeszcze skorzystać z jednej bardzo przydatnej funkcji pythona. Bardzo często kiedy wyświetlamy jakiś komunikat na ekranie, chcielibyśmy w standardowym dłuższym tekście odpowiednio dopasować tylko jego krótki fragment. Na przykład gdybyśmy chcieli przywitać użytkownika, standardowy tekst mógłby brzmieć Witaj ?, miło Cię zobaczyć znowu! gdzie w miejsce znaku zapytania wstawiałoby się tylko imię użytkownika.

Do tego celu można się posłużyć następującą konstrukcją:

>>>print("\nNie będę śpiewał %s kołysanek na lekcji\n" % "dziwnych")

Nie będę śpiewał dziwnych kołysanek na lekcji

>>>

Co zrobiliśmy? W tym miejscu tekstu, gdzie chcielibyśmy wstawić inny, umieściliśmy znacznik %s i dalej, już poza tekstem, po znaku % podaliśmy wartość, którą należy wstawić w zaznaczone miejsce. Posłużmy się jeszcze innym przykładem, w którym wstawiamy dwa różne teksty do innego.

>>>print("\nNie będę śpiewał %s %s kołysanek na lekcji\n" % ("przerażająco", "dziwnych"))

Nie będę śpiewał przerażająco dziwnych kołysanek na lekcji

>>>

A co jeśli chcielibyśmy wstawić nie tekst, ale liczbę? Wówczas musimy zastosować inny znacznik, czyli %d.

>>> print("\nNie będę śpiewał %s %s kołysanek na %d lekcji\n" % ("przerażająco", "dziwnych", 5))

Nie będę śpiewał przerażająco dziwnych kołysanek na 5 lekcji

>>>

W tej chwili powyższa konstrukcja może się wydawać nadmiarowa -- widać, że prościej byłoby zapisać całe zdanie od razu tak jak miało brzmieć. Ale jej duża przydatność wyjaśni się już w następnym punkcie.

zmienna, czyli jak pamiętać to co program przetwarza

Byłoby bardzo wygodnie, gdyby dało się napisać instrukcję powodującą zapisanie w pamięci komputera jakiegoś wyniku, który za chwilę wykorzystamy w dalszym ciągu programu. Taki podręczny "schowek" na dane to zmienna. W pythonie jej stworzenie jest zupełnie banalne, na przykład:

>>> jakas_liczba = 5
>>>

Wygląda tak, jakby nic się nie stało, ale wystarczy wpisać nazwę nowo zdefiniowanej zmiennej, a python wyświetli jej wartość, co oznacza, że ją zapamiętał!

>>> jakas_liczba
5
>>>

Co więcej, od teraz wszędzie, gdzie powołamy się na nazwę zmiennej otrzymamy jej aktualną wartość.

>>> jakas_liczba + 7
12
>>> jakas_liczba - 3
2
>>> print(":" + ")" * jakas_liczba)
:)))))
>>>

Ta prosta konstrukcja języka okazuje się bardzo użyteczna. Za chwilę poznamy różne inne zastosowania, ale już teraz możemy powiedzieć, że zmienna będzie się pojawiała co chwilę i bez niej wielu przydatnych rzeczy nie dałoby się wykonać.

Ponieważ wiemy już jak działa zmienna możemy wrócić do przykładu drukowania napisów, w którym wstawialiśmy do drukowanego tekstu inne treści. Załóżmy, że mierzymy co 1/10 sekundy prędkość naszego pojazdu i chcemy ją wyświetlić zarówno w metrach na sekundę jak i w kilometrach na godzinę. Jeśli wynik pomiaru zapiszemy sobie w zmiennej predkosc wówczas następująca instrukcja pozwoli nam drukować aktualnie zmierzoną wartość w sposób zrozumiały i wygodny dla człowieka (dla przykładu zmieniamy wartość prędkości dwa razy, żeby zasymulować różne pomiary):

>>> predkosc = 3.21
>>> print("\nAktualna prędkość wynosi %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))

Aktualna prędkość wynosi 3.210000 [m/s] (= 11.556000 [km/h])

>>> predkosc = 5.111
>>> print("\nAktualna prędkość wynosi %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))

Aktualna prędkość wynosi 5.111000 [m/s] (= 18.399600 [km/h])

>>>

Zwróć uwagę, że do wyświetlania liczb z przecinkiem używamy znacznika %f.

Nieco dłuższe programy

Powyżej widzieliśmy instrukcje, które mieszczą się w jednej linijce. Moglibyśmy kontynuować pisanie programów w ten sposób, ale za chwilę stałoby się to mało wygodne, a już na pewno mało czytelne. Musimy więc wyjaśnić jak pracować z dłuższymi programami. Na szczęście jest to całkiem proste.

Całe zadanie sprowadza się do zapisania tekstu programu w pliku i potem przekazania go interpreterowi pythona do wykonania.

Dla przykładu weźmy ostatni przykład powyżej. Składa się z dwóch instrukcji: ustawienia wartości zmiennej predkosc i wyświetlenia jej wartości. Otwórzmy więc dowolny edytor tekstu (na przykład gedit) i w nowym pliku wstawmy te dwie instrukcje:

 # coding: utf-8
 
 predkosc = 5.111
 print("\nAkualna prędkość wynosi %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))

Teraz zapiszmy plik z dowolną nazwą na dysku. Warto dać mu rozszerzenie .py (jak python), które jest standardowym oznaczeniem dla programów w pythonie. Jeśli tak zrobimy gedit powinien to zauważyć i zacząć kolorować składnię dzięki czemu kod będzie czytelniejszy.

Jeśli użyliśmy nazwy pliku pierwszy.py możemy wykonać program wywołując polecenie:

ktos@maszyna:~ $ python pierwszy.py

Akualna prędkość wynosi 5.111000 [m/s] (= 18.399600 [km/h])

ktos@maszyna:~ $

Jak widać program działa tak samo jak w powyżej.

Natomiast teraz możemy umieścić w pliku nie jedną, ale wiele instrukcji i napisać dzięki temu dłuższy kod robiący ciekawsze rzeczy!

Dodatkowa pierwsza linijka # coding: utf-8 jest potrzebna dlatego, że nasz tekst używa polskich znaków diakrytycznych (słowo prędkość ma trzy), a bez ustawienia kodowania uwzględniającego polskie znaki python nie spodziewa się ich i zamiast wykonać program wyświetli jedynie rozpaczlliwy komunikat, że nie rozumie znaków spoza standardu ASCII (stary standard kodowania znaków jeszcze z poprzedniego wieku, nowsza 3 wersja pythona domyślnie używa bardziej uniwersalnego UTF-8):

File "/home/ktos/pierwszy.py", line 2
SyntaxError: Non-ASCII character '\xc4' in file /home/ktos/pierwszy.py on line 2, 
                       but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

Przy okazji zauważmy, że to co znajduje się w linijce po znaku # traktowane jest przez interpreter jako komentarz, a więc to co jest tam napisane nie będzie wykonywane jako program. Linie komentarza przydają się, żeby choćby samemu sobie napisać wskazówkę o co chodziło w kodzie, który jest w pobliżu. W bardzo długich programach można się szybko pogubić i rozsądnie napisany komentarz bardzo ułatwia życie. Na przykład:

 # coding: utf-8
 
 predkosc = 5.111
 # Przelicznik 3.6 bierze się stąd, że 1 [km/h] =  (1000 [m] / 3600 [s] = 1000/3600 [m/s] ) = 1/3.6 [m/s], 
 # skąd wynika odwrotna zależność że 1 [m/s] = 3.6 [km/h]
 print("\nAkualna prędkość wynosi %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))

Kilka wielolinijkowych konstrukcji języka python

Skoro potrafimy już używać programów składających się z wielu linijek zajmijmy się dłuższymi konstrukcjami języka.

instrukcja warunkowa, czy co robić jeśli tak, a co jeśli nie

Wracając do przykładu z rozmów między ludźmi dość często przekazując instrukcje używamy poleceń warunkowych nawet nie bardzo uświadamiając sobie ten fakt. Często uzależniamy działanie od jakiegoś wstępnego warunku mówiąc na przykład: Jeśli X zadzwoni o osiemnastej to będę musiał ..., a jak nie zadzwoni to możemy ....

Dokładnie tak samo możemy chcieć zaprogramować komputer. Na przykład jeśli na przeciwko robota stoi przeszkoda (sprawdzamy co podaje jakiś czujnik) to należy wykonać jej omijanie, a jeśli przeszkody nie ma, można kontynuować jazdę do przodu.

Do sterowania programem w ten sposób służy właśnie instrukcja warunkowa, której schemat przedstawiony jest poniżej.

if warunek:
    # instrukcje do wykonania 
    # jeśli warunek jest spełniony
    ...
else:
    # a tu instrukcje w przeciwnym przypadku
    # czyli jeśli warunek nie jest spełniony
    ...

# ciąg dalszy programu
...

Zastosujmy instrukcję warunkową do modyfikacji wyświetlania komunikatu powyżej:

# coding: utf-8

predkosc = 5.111

if predkosc < 50:
    # wyświetlamy tak samo jeśli zmierzona prędkość wydaje się rozsądna
    print("\nAkualna prędkość wynosi %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))    
else:
    # Robot przypadkiem wjechał na tory Pendolino?
    print("\nCzujnik zwariował!!! Ten robot nie może jechać z prędkością %f [m/s] (= %f [km/h])\n" % (predkosc, predkosc * 3.6))

Spróbuj zapisać ten program z różnymi wartościami prędkości i wykonać.

W przypadku wielolinijkowych konstrukcji pythona trzeba zwrócić uwagę na wcięcia linijek. Nie przypadkiem te instrukcje, które mają się wykonać dla spełnionego, i osobno dla niespełnionego, warunku są przesunięte o 4 spacje w prawo. Tylko dzięki temu przesunięciu interpreter pythona wie, które instrukcje wykonywać w zależności od spełnionego warunku. Popatrzmy na taki przykład:

# coding: utf-8

poziom_energii = 2.0

if poziom_energii < 1.0:  # Z powodu wcięcia tekstu programu dwie następne instrukcje wykonają się zależnie od warunku.
    print("UWAGA: Energia się kończy!") 
    print("Najbliższa stołówka za rogiem!") 

print("Aktualnie masz %f kcal energii." % poziom_energii) # Ta instrukcja nie zależy od warunku i wykona się zawsze.

Jak zwykle warto uruchomić ten program ze zmienną poziom_energii mniejszą niż 1.0 żeby zobaczyć jego działanie w dwóch różnych przypadkach.

pętla, czyli to co komputer potrafi robić bez znudzenia

Bardzo często chcemy jakieś instrukcje powtórzyć określoną ilość razy. Na przykład chcielibyśmy po kolei zapalić diody podłączone do wyjść GPIO o numerach od 5 do 15. Moglibyśmy zapisać 10 następujących poleceń:

# pominięte instrukcje wczytujące moduł obsługi wyjść gpio oraz inicjalizujące płytkę 
# 

gpio.output(5, 1)
gpio.output(6, 1)
gpio.output(7, 1)
gpio.output(8, 1)
gpio.output(9, 1)
gpio.output(10, 1)
gpio.output(11, 1)
gpio.output(12, 1)
gpio.output(13, 1)
gpio.output(14, 1)
gpio.output(15, 1)

Jednym słowem -- horror. Gdybyśmy chcieli zapalić na przykład 1024*768 diod stanowiących ekran komputera, stosując taki sposób kodowania, musielibyśmy napisać blisko milion instrukcji!!!

Ale można to zrobić prościej. Wystarczy zastosować pętlę. Powyższy program napisany z jej użyciem jest o wiele krótszy:

# pominięte instrukcje wczytujące moduł obsługi wyjść gpio oraz inicjalizujące płytkę 
# ....

for numer in range(5, 16):  
    gpio.output(numer, 1)

Co tu się dzieje dokładnie? Przede wszystkim funkcja range (ang. zakres) zwraca listę numerów z podanego przedziału z pominięciem ostatniego elementu. Sprawdźmy na przykład co zwróci użyte powyżej range(5,16):

>>> range(5,16)
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>>

Jak widać na końcu nie ma liczby 16.

Po angielsku for oznacza dla, in to po polsku w. Jeśli przepiszemy tekst programu po polsku, jego działanie stanie się praktycznie oczywiste

dla numer w zakresie(5, 16):  
    gpio.output(numer, 1)


czyli wykonaj instrukcję gpio.output(numer, 1) dla zmiennej numer ustawionej kolejno na liczby z podanego zakresu. Proste prawda?

Jeśli chciałoby się sprawdzić, że rzeczywiście zmienna numer zmienia się w podanym zakresie wystarczy ją wydrukować poznaną wcześniej instrukcją print.

>>> for numer in range(5,16): print(numer);   
... 
5
6
7
8
9
10
11
12
13
14
15
>>>

Załóżmy, że chcielibyśmy mrugnąć jedną diodą 20 razy. Nic prostszego! Wystarczy pętla o 20 krokach w środku której wykonamy jedno pełne mrugnięcie (a więc zapalenie, odczekanie chwilę, zgaszenie i znów odczekanie):

# pominięte instrukcje wczytujące moduł obsługi wyjść gpio, moduł obsługi czasu time, oraz inicjalizujące płytkę 
# ...

for powtorka in range(1, 21):  # Pamiętaj, że range(1, 21) zwróci [1,2,...,20], bez 21. 
    gpio.output(numer, 1)
    time.sleep(0.5)
    gpio.output(numer, 0)
    time.sleep(0.5)

Wielokrotnie taka konstrukcja się przyda. Na przykład, kiedy będziemy chcieli wykonać dokładnie 35 kroków silnika krokowego zamontownego po lewej stronie robota zapewne użyjemy pętli podobnej do takiej:

# pominięte instrukcje wczytujące moduł obsługi silnika
# ...

for powtorka in range(1, 36):  
    krok(SILNIK_LEWY, DO_PRZODU)