Zanurkuj w Pythonie/Pętla for

Z Wikibooks, biblioteki wolnych podręczników.

Pętla for[edytuj]

Podobnie jak wiele innych języków, Python posiada pętlę for. Jedynym powodem, dla którego nie widzieliśmy tej pętli wcześniej jest to, że Python posiada tyle innych użytecznych rzeczy, że nie potrzebujemy jej aż tak często.

Wiele języków programowania nie posiada typu danych o takich możliwościach, jakie daje lista w Pythonie, dlatego też w tych językach trzeba wykonywać dużo manualnej pracy, trzeba określać początek, koniec i krok, aby przejść zakres liczb całkowitych, znaków lub innych iteracyjnych jednostek. Jednak pythonowa pętla for, przechodzi całą listę w identyczny sposób, jak ma to miejsce w wyrażeniach listowych.


Przykład. Wprowadzenie do pętli for
>>> li = ['a', 'b', 'e']
>>> for s in li:         #(1)
...     print s          #(2)
a
b
e
>>> print "\n".join(li)  #(3)
a
b
e
  1. Składnia pętli for jest bardzo podobna do składni wyrażenia listowego. li jest listą, a zmienna s będzie przyjmować kolejne wartości elementów tej listy podczas wykonywania pętli, zaczynając od elementu pierwszego.
  2. Podobnie jak wyrażenie if i inne bloki tworzone za pomocą wcięć, pętla for może posiadać dowolną liczbę linii kodu.
  3. To główna przyczyna, dla której jeszcze nie widzieliśmy pętli for. Po prostu jej nie potrzebowaliśmy. Jest to zadziwiające, jak często trzeba wykorzystywać pętle for w innych językach, podczas gdy tak naprawdę potrzebujemy metody join, czy też wyrażenia listowego.


Przykład. Prosty licznik
>>> for i in range(3):             #(1)
...     print i
0
1
2
>>> li = ['a', 'b', 'c', 'd']
>>> for i in range(len(li)):       #(2)
...     print li[i]
a
b
c
d
  1. Jak zobaczyliśmy w przykładzie 3.23, "Przypisywanie kolejnych wartości", funkcja range tworzy listę liczb całkowitych, a tę listę będziemy chcieli przejść za pomocą pętli. Powyższy kod być może wygląda trochę dziwacznie, lecz bywa przydatny do tworzenia pętli licznikowej.
  2. Nigdy tego nie róbmy. Jest to visualbasicowy styl myślenia. Skończmy z nim. Powinno się iterować listę, jak pokazano to w poprzednim przykładzie.

Pętle for nie zostały stworzone do tworzenia prostych pętli licznikowych. Służą one raczej do przechodzenia po wszystkich elementach w danym obiekcie. Poniżej pokazano przykład, jak można iterować po słowniku za pomocą pętli for:

Przykład. Iterowanie po elementach słownika
>>> import os
>>> for k, v in os.environ.items():      #(1) (2)
...     print ("%s=%s" % (k, v))
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim 
...
>>> print ("\n".join(["%s=%s" % (k, v) for k, v in os.environ.items()]) ) #(3)
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim
...
  1. os.environ jest słownikiem zmiennych środowiskowych zdefiniowanych w systemie operacyjnym. W Windowsie mamy tutaj zmienne użytkownika i systemu dostępne z MS-DOS-a. W Uniksie mamy tu zmienne wyeksportowane w twojej powłoce przez skrypty startowe. W systemie Mac OS nie ma czegoś takiego jak zmienne środowiskowe, więc słownik ten jest pusty.
  2. os.environ.items() zwraca listę krotek w postaci [(klucz1, wartosc1), (klucz2, wartosc2), ...]. Pętla for iteruje po tej liście. W pierwszym przebiegu pętli, przypisuje ona wartość klucz1 do zmiennej k, a wartość wartosc1 do v, więc k = "USERPROFILE", a v = "C:\Documents and Settings\mpilgrim". W drugim przebiegu pętli, k przyjmuje wartość drugiego klucza, czyli "OS", a v bierze odpowiadającą temu kluczowi wartość, czyli "Windows_NT".
  3. Za pomocą wielozmiennego przypisania i wyrażeń listowych, możemy zastąpić całą pętle for jednym wyrażeniem. Z której metody będziemy korzystać w kodzie, jest kwestią stylu pisania. Niektórym się to może podobać, ponieważ za pomocą wyrażenia listowego jasno stwierdzamy, że to co robimy to odwzorowywanie słownika na listę, a następnie łączymy otrzymaną listę w jeden napis. Inni z kolei preferują pisanie z użyciem pętli for. Otrzymane wyjście programu jest identyczne w obydwu przypadkach, jakkolwiek druga wersja w tym przykładzie jest nieco szybsza, ponieważ tylko raz wykorzystujemy instrukcję print.

Spójrzmy teraz na pętlę for w MP3FileInfo z przykładu fileinfo.py wprowadzonego w rozdziale 5.


Przykład. Pętla for w MP3FileInfo
    tagDataMap = {u"tytuł"     : (  3,  33, stripnulls),
                   "artysta"   : ( 33,  63, stripnulls),
                   "album"     : ( 63,  93, stripnulls),
                   "rok"       : ( 93,  97, stripnulls),
                   "komentarz" : ( 97, 126, stripnulls),
                   "gatunek"   : (127, 128, ord)}                              #(1)
    # [...]
            if tagdata[:3] == 'TAG':
                for tag, (start, end, parseFunc) in self.tagDataMap.items():  #(2)
                    self[tag] = parseFunc(tagdata[start:end])                 #(3)
  1. tagDataMap jest atrybutem klasy, który definiuje tagi, jakie będziemy szukali w pliku MP3. Tagi są przechowywane w polach o ustalonej długości. Ponieważ czytamy ostatnie 128 bajtów pliku, bajty od 3 do 32 z przeczytanych danych są zawsze tytułem utworu, bajty od 33 do 62 są zawsze nazwą artysty, od 63 do 92 mamy nazwę albumu itd. Zauważmy, że tagDataMap jest słownikiem krotek, a każda krotka przechowuje dwie liczby całkowite i jedną referencję do funkcji.
  2. Wygląda to skomplikowanie, jednak takie nie jest. Struktura zmiennych w pętli for odpowiada strukturze elementów listy zwróconej poprzez metodę items. Pamiętamy, że items zwraca listę krotek w formie (klucz, wartosc). Jeśli pierwszym elementem tej listy jest (u"tytuł", (3, 33, <function stripnulls at 0xb7c91f7c>)), tak więc podczas pierwszego przebiegu pętli, tag jest równe u"tytuł", start przyjmuje wartość 3, end wartość 33, a parseFunc zawiera funkcję stripnulls.
  3. Kiedy już wydobędziemy wszystkie parametry tagów pliku MP3, zapisanie danych odnoszących się do określonych tagów będzie proste. W tym celu wycinamy napis tagdata od wartości w zmiennej start do wartości w zmiennej end, aby pobrać aktualne dane dla tego tagu, a następnie wywołujemy funkcję parseFunc, aby przetworzyć te dane, a potem przypisujemy zwróconą wartość do klucza tag w słowniku self (ściślej, self jest instancją podklasy słownika). Po przejściu wszystkich elementów w tagDataMap, self będzie przechowywał wartości dla wszystkich tagów, a my będziemy mogli się dowiedzieć o utworze, co tylko będziemy chcieli.