Zanurkuj w Pythonie/Pętla for
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.
for
>>> li = ['a', 'b', 'e'] >>> for s in li: #(1) ... print s #(2) a b e >>> print "\n".join(li) #(3) a b e
- Składnia pętli
for
jest bardzo podobna do składni wyrażenia listowego.li
jest listą, a zmiennas
będzie przyjmować kolejne wartości elementów tej listy podczas wykonywania pętli, zaczynając od elementu pierwszego. - Podobnie jak wyrażenie
if
i inne bloki tworzone za pomocą wcięć, pętlafor
może posiadać dowolną liczbę linii kodu. - 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ętlefor
w innych językach, podczas gdy tak naprawdę potrzebujemy metodyjoin
, czy też wyrażenia listowego.
>>> 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
- 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. - 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
:
>>> 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 ...
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.os.environ.items()
zwraca listę krotek w postaci[(klucz1, wartosc1), (klucz2, wartosc2), ...]
. Pętlafor
iteruje po tej liście. W pierwszym przebiegu pętli, przypisuje ona wartośćklucz1
do zmiennejk
, a wartośćwartosc1
dov
, więck = "USERPROFILE"
, av = "C:\Documents and Settings\mpilgrim"
. W drugim przebiegu pętli,k
przyjmuje wartość drugiego klucza, czyli"OS"
, av
bierze odpowiadającą temu kluczowi wartość, czyli"Windows_NT"
.- 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ętlifor
. 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.
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)
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, żetagDataMap
jest słownikiem krotek, a każda krotka przechowuje dwie liczby całkowite i jedną referencję do funkcji.- 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, żeitems
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ówneu"tytuł"
,start
przyjmuje wartość3
,end
wartość33
, aparseFunc
zawiera funkcjęstripnulls
. - 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 zmiennejstart
do wartości w zmiennejend
, 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 kluczatag
w słownikuself
(ściślej,self
jest instancją podklasy słownika). Po przejściu wszystkich elementów wtagDataMap
,self
będzie przechowywał wartości dla wszystkich tagów, a my będziemy mogli się dowiedzieć o utworze, co tylko będziemy chcieli.