5         LEKCJA 5 - TYPY SEKWENCYJNE

5.1        Typy sekwencyjne

Po uruchomieniu IDLE'a zgłasza się tryb interaktywny Pythona.

Wykorzystamy go do poznania pythonowskich sekwencyjnych typów danych.

Sekwencyjne typy danych służą do zapamiętywania wielu wartości w pojedynczej zmiennej, w odróżnieniu od typów prostych, takich jak int, które w pojedynczej zmiennej mogą zachować tylko jedną wartość.

5.2        Typ napisowy

Do tej pory poznaliśmy już jeden typ sekwencyjny. Jest nim typ napisowy (string) - patrz punkty 2.13-2.16. Przypomnijmy, że wartości napisów podajemy w cudzysłowach lub apostrofach:

 

>>> txt="napis"

>>> txt2='napis'

 

I, że możemy na nich wykonywać pewne operacje, np.:

 

>>> txt2+='owi nierówny'

>>> print txt2

napisowi nierówny

 

Napisy są sekwencjami znaków. Każdy typ sekwencyjny pozwala na dostęp do każdego swojego elementu z osobna. Aby uzyskać dostęp do znaku na określonej pozycji podajemy jej indeks (numer porządkowy liczony od lewej, zero oznacza pierwszy znak napisu) w nawiasach kwadratowych bezpośrednio po napisie:

 

>>> "abc"[0]

'a'

>>> "abc"[1]

'b'

>>> "abc"[2]

'c'

>>> txt[0]

'n'

>>> txt[1]

'a'

>>> txt[2]

'p'

>>> txt[3]

'i'

>>> txt[4]

's'

>>> txt[5]

 

Traceback (most recent call last):

  File "<pyshell#22>", line 1, in -toplevel-

    txt[5]

IndexError: string index out of range

>>>

 

Powodem błędu była próba odczytania znaku o zbyt wysokim numerze, którego w napisie nie ma. Aby poznać długość napisu, posługujemy się funkcją len:

 

>>> len("abc")

3

>>> len(txt)

5

>>> len(txt2)

17

>>> len(txt*20)

100

>>>

 

Zliczać znaki możemy także od końca napisu w prawo. Używamy w tym celu indeksów ujemnych (-1 oznacza ostatni znak napisu):

 

>>> "abc"[-1]

'c'

>>> "abc"[-2]

'b'

>>> "abc"[-3]

'a'

>>> txt[-1]

's'

>>>

 

5.3        Kody ASCII

Pojedynczy znak zapisany jest jako liczba odpowiadająca kodowi ASCII określonego symbolu graficznego.

Aby poznać kod ASCII określonego znaku, należy użyć funkcji ord:

 

>>> ord('A')

65

>>> ord('a')

97

>>> ord("1")

49

>>> ord("2")

50

 

Parametrem funkcji ord musi być pojedynczy znak, a nie wieloznakowy napis:

 

>>> ord(txt[0])

110

>>> ord(txt)

 

Traceback (most recent call last):

  File "<pyshell#52>", line 1, in -toplevel-

    ord(txt)

TypeError: ord() expected a character, but string of length 5 found

>>>

 

Aby zamienić kod ASCII na odpowiadający mu znak, używamy funkcji chr:

 

>>> chr(65)

'A'

>>> chr(32)

' '

>>> chr(10)

'\n'

>>> chr(49)+chr(48)+chr(50)

'102'

>>>

5.4        Fragmenty napisu

Czasami interesuje nas nie pobranie z napisu pojedynczego znaku, ale wykrojenie ciągu znaków. Do wykrajania fragmentów napisów używamy zapisu z dwukropkiem:

 

Fragment napisu do ósmego znaku:

 

>>> txt2[:8]

'napisowi'

 

Fragment napisu od dziesiątego znaku:

 

>>> print txt2[9:]

nierówny

 

Fragment napisu od szóstego do dwunastego znaku:

 

>>> txt2[5:12]

'owi nie'

 

Co drugi znak z fragmentu napisu od szóstego do dwunastego znaku:

 

>>> txt2[5:12:2]

'oine'

 

Możemy także używać indeksów ujemnych:

 

>>> txt2[:-9]

'napisowi'

>>> txt2[-8:]

'nier\xf3wny'

>>> txt2[-12:-5]

'owi nie'

>>> txt2[-12:-5:2]

'oine'

5.5        Typ napisowy jako typ niezmienny

Sekwencyjne typy danych w Pythonie dzielimy na zmienne (mutable) i niezmienne (immutable). Typ napisowy należy do typów niezmiennych.

Typy niezmienne nie mogą zmieniać swojej wartości. A zatem zapis:

 

>>> txt+=chr(32)+txt2

>>> print txt

napis napisowi nierówny

 

tak naprawdę nie zmienia wartości zmiennej txt, ale tworzy nową zmienną o tej samej nazwie, a innej wartości, starą usuwając.

Konsekwencją niezmienności typu napisowego jest niemożność zmiany jego elementu:

 

>>> txt[2]='w'

 

Traceback (most recent call last):

  File "<pyshell#41>", line 1, in -toplevel-

    txt[2]='w'

TypeError: object does not support item assignment

>>>

 

ani fragmentu:

 

>>> txt[2:4]='wis'

 

Traceback (most recent call last):

  File "<pyshell#93>", line 1, in -toplevel-

    txt[2:4]='wis'

TypeError: object doesn't support slice assignment

>>>

 

Oczywiście, pożądaną operację możemy wykonać, inaczej formułując polecenie:

 

>>> txt2=txt[:2]+"w"+txt[3:]

>>> print txt2

nawis napisowi nierówny

>>>

5.6        Inne typy sekwencyjne

Napisy mogą przechowywać sekwencje znaków. Czasami potrzebujemy jednak zapamiętać sekwencje danych nie będących znakami, ale np. liczbami lub innymi sekwencjami.

Do przechowywania takich sekwencji służą w Pythonie: krotki (tuples) i listy (lists).

Elementy zarówno krotek, jak i list mogą być dowolnego typu.

Krotki są typem niezmiennym. Oznacza to, że mają z góry ustaloną długość, a zmiana wartości poszczególnych elementów z osobna nie jest możliwa.

Listy są typem zmiennym. Oznacza to, że można je łatwo skracać i wydłużać i jest możliwa zmiana wartości poszczególnych elementów z osobna.

5.7        Tworzenie i używanie list

Listy tworzymy używając nawiasów kwadratowych, rozdzielając ich elementy przecinkami.

Aby stworzyć listę trzech liczb naturalnych, napiszemy:

 

>>> lista1 = [1, 2, 3]

>>> lista1

[1, 2, 3]

 

Aby stworzyć listę złożoną z czterech napisów, napiszemy:

 

>>> lista2 = ["pierwszy", txt, txt2, "ostatni"]

>>> lista2

['pierwszy', 'napis napisowi nier\xf3wny', 'nawis napisowi nier\xf3wny', 'ostatni']

 

Listy mogą zawierać elementy różnych typów:

 

>>> lista3 = [1.0, 2, "trzy"]

>>> lista3

[1.0, 2, 'trzy']

 

Listy mogą zawierać inne listy:

 

>>> lista4=[ [1, 2, 3], ["Nocny", "Dzienny"] ]

>>> lista4

[[1, 2, 3], ['Nocny', 'Dzienny']]

 

Listy mogą być puste:

 

>>> lista_pusta = []

>>> lista_pusta

[]

>>> len(lista_pusta)

0

 

Listy mogą zawierać tylko jeden element:

 

>>> lista_jednoelementowa = [1]

>>> lista_jednoelementowa

[1]

 

Można odczytywać wybiórczo zawartość poszczególnych elementów listy:

 

>>> lista1[0]

1

>>> lista2[-1]

'ostatni'

 

Lub ich ciągów:

 

>>> lista1[1:]

[2, 3]

>>> lista2[0::3] # co trzeci element listy

['pierwszy', 'ostatni']

>>> lista4[1:]

[['Nocny', 'Dzienny']]

 

Listy można powielać:

 

>>> lista2*=2

>>> lista2

['pierwszy', 'napis napisowi nier\xf3wny', 'nawis napisowi nier\xf3wny', 'ostatni', 'pierwszy', 'napis napisowi nier\xf3wny', 'nawis napisowi nier\xf3wny', 'ostatni']

>>> []*1000

[]

>>> [1,2,3]*0

[]

 

Określać ich długość:

 

>>> len(lista2)

8

 

Skracać:

 

>>> lista2=lista2[:3]

>>> lista2

['pierwszy', 'napis napisowi nier\xf3wny', 'nawis napisowi nier\xf3wny']

 

Wydłużać:

 

>>> lista2+=['ostatni']

>>> lista2

['pierwszy', 'napis napisowi nier\xf3wny', 'nawis napisowi nier\xf3wny', 'ostatni']

5.8        Modyfikacja list

Listy są sekwencjami zmiennymi, można więc modyfikować ich fragmenty:

 

>>> lista3

[1.0, 2, 'trzy']

>>> lista3[0:2]=["jeden","dwa"]

>>> lista3

['jeden', 'dwa', 'trzy']

 

lub pojedyncze elementy:

 

>>> lista1

[1, 2, 3, 4]

>>> lista1[2]+=1

>>> lista1

[1, 2, 4, 4]

 

Można też usuwać elementy ze środka listy, tak samo jak w sekwencjach niezmiennych:

 

>>> lista2=lista2[:2]+lista2[3:]

>>> lista2

['pierwszy', 'napis napisowi nier\xf3wny', 'ostatni']

 

Lub prościej, z użyciem instrukcji del, np.:

 

>>> del lista2[1]

>>> lista2

['pierwszy', 'ostatni']

5.9        Porównywanie list

Porównywanie list odbywa się na zasadzie porównywania poszczególnych elementów:

-         jeżeli elementy obu list są sobie równe, listy są równe

-         jeżeli listy różnią się choć jednym elementem, to są nierówne

-         jeżeli pierwszy element pierwszej listy jest większy od pierwszego elementu drugiej listy, to pierwsza lista jest większa od drugiej

-         jeżeli pierwszy element pierwszej listy jest taki sam jak pierwszy element drugiej listy, decyduje porównanie drugich elementów, itd.

-         element nieistniejący jest zawsze mniejszy od każdego innego elementu

 

>>> lista1 == [1, 2, 4, 4]

True

>>> lista1 != [1, 2, 3, 4]

True

>>> lista1 > [1, 2, 2, 5]

True

>>> lista1 < [1, 2, 4, 4, 5]

True

5.10    Sprawdzanie zawartości list

Aby sprawdzić, czy określona wartość znajduje się na liście, używamy operatora in:

 

>>> 1 in lista1

True

>>> 2 not in lista1

False

>>> "pierwszy" in lista2

True

5.11    Listy wielopoziomowe

W przypadku list wielopoziomowych, to jest takich, które zawierają inne listy, np.:

 

>>> lista4

[[1, 2, 3], ['Nocny', 'Dzienny']]

 

możliwy jest dostęp do poszczególnych elementów list podrzędnych poprzez użycie dwóch indeksów:

 

>>> lista4[1][0]

'Nocny'

>>> lista4[0][1]

2

 

Jako pierwszy podajemy zawsze indeks listy wyższego rzędu.

5.12    Typ listy jako typ zmienny

W Pythonie wszystkie sekwencje zmienne nie odnoszą się do określonych danych, ale do miejsca w pamięci, w którym te dane się znajdują.

W związku z tym przypisanie listy do listy nie kopiuje wartości, a jedynie wskaźnik do nich:

 

>>> lista5=lista1

>>> lista5

[1, 2, 4, 4]

 

A zatem od tej pory, niezależnie czy zmieniamy elementy listy 1 czy 5, zmieniamy obie, bo zmianie tak naprawdę podlegają te same dane:

 

>>> lista1[2]=3

>>> lista5

[1, 2, 3, 4]

>>> lista5[2]=5

>>> lista1

[1, 2, 5, 4]

 

Jeżeli listę utworzono z innych list:

 

>>> lista6=[0,lista1]

>>> lista6

[0, [1, 2, 5, 4]]

 

To każda zmiana ich wartości będzie przenoszona na listę nadrzędną:

 

>>> lista1[1]=4

>>> lista6

[0, [1, 4, 5, 4]]

 

Co więcej, zmiana wartości listy nadrzędnej będzie przenoszona na listę podrzędną:

 

>>> lista6[1][0]=2

>>> lista1

[2, 4, 5, 4]

5.13    Tworzenie i używanie krotek

Krotki pod wieloma względami przypominają listy, w podobny sposób tworzymy je i sprawdzamy ich wartości. W odróżnieniu od list, krotki są sekwencjami niezmiennymi, co powoduje różnice w sposobie ich modyfikacji.

Krotki tworzymy używając nawiasów okrągłych, rozdzielając ich elementy przecinkami.

Aby stworzyć krotkę z trzech liczb naturalnych, napiszemy:

 

>>> krotka1=(1,2,3)

>>> krotka1

(1, 2, 3)

 

Przy czym nawiasy okrągłe można pomijać:

 

>>> krotka1=1,2,3

>>> krotka1

(1, 2, 3)

 

(Dla zachowania przejrzystości my tego robić nie będziemy.)

 

Krotki mogą zawierać elementy różnych typów:

 

>>> krotka2=(1.0, 2, "trzy")

>>> krotka2

(1.0, 2, 'trzy')

 

W tym sekwencyjnych

 

>>> krotka3=(krotka1, lista1)

>>> krotka3

((1, 2, 3), [2, 4, 5, 4])

 

Krotki mogą być puste:

 

>>> krotka_pusta = ()

>>> krotka_pusta

()

>>> len (krotka_pusta)

0

 

Krotki mogą zawierać tylko jeden element. Jako że zapis (1) oznacza liczbę jeden w nawiasie, tworząc krotki jednoelementowe obowiązkowo na ich końcu stawiamy przecinek:

 

>>> liczba=(1)

>>> liczba

1

>>> krotka_jednoelementowa=(1,)

>>> krotka_jednoelementowa

(1,)

>>> len(krotka_jednoelementowa)

1

>>>

 

Można odczytywać wybiórczo zawartość poszczególnych elementów krotki:

 

>>> krotka1[1]

2

>>> krotka3[-1]

[2, 4, 5, 4]

 

Lub ich ciągów:

 

>>> krotka1[1:]

(2, 3)

>>> krotka1[::2] # co drugi element krotki

(1, 3)

 

Krotki można powielać:

 

>>> krotka1*=2

>>> krotka1

(1, 2, 3, 1, 2, 3)

 

Skracać:

 

>>> krotka1=krotka1[:3]

>>> krotka1

(1, 2, 3)

 

Wydłużać:

 

>>> krotka1=krotka1+(4,)

>>> krotka1

(1, 2, 3, 4)

5.14    Modyfikacja krotek

Krotki są sekwencjami niezmiennymi, więc nie można modyfikować ich fragmentów:

 

>>> krotka1[2]=1

 

Traceback (most recent call last):

  File "<pyshell#151>", line 1, in -toplevel-

    krotka1[2]=1

TypeError: object does not support item assignment

 

Oczywiście, pożądaną operację możemy wykonać, inaczej formułując polecenie:

 

>>> krotka1=krotka1[:2]+(1,)+krotka1[3:]

>>> krotka1

(1, 2, 1, 4)

5.15    Typ krotki jako typ niezmienny

Krotki są sekwencjami niezmiennymi, w związku z tym przypisanie krotki do krotki kopiuje faktyczne wartości, a nie jedynie wskaźnik do nich:

 

>>> krotka4=krotka1

>>> krotka4

(1, 2, 1, 4)

>>> krotka1=(1,2,3,4)

>>> krotka4

(1, 2, 1, 4)

5.16    Konwersja typów sekwencyjnych

W konwersji typów sekwencyjnych używamy następujących instrukcji:

 

-         list zamienia typ sekwencyjny na listę

 

>>> lista7=list(krotka5)

>>> lista7

[2, 4, 5, 4]

>>> lista8=list("abcd")

>>> lista8

['a', 'b', 'c', 'd']

 

-         tuple zamienia typ sekwencyjny na krotkę

 

>>> krotka5=tuple(lista1)

>>> krotka5

(2, 4, 5, 4)

>>> krotka6=tuple("abcd")

>>> krotka6

('a', 'b', 'c', 'd')

 

-         str zamienia typ sekwencyjny na napis

 

>>> str(krotka6)

"('a', 'b', 'c', 'd')"

>>> str(lista7)

'[2, 4, 5, 4]'

 

Lub krócej, przy użyciu odwróconych apostrofów:

 

>>> `krotka6`

"('a', 'b', 'c', 'd')"

>>> `lista7`

'[2, 4, 5, 4]'

5.17    Pętle iterowane po elementach sekwencji

Aby wykonać jakieś operacje na wszystkich lub wybranych elementach sekwencji, najprościej jest posłużyć się pętlą for ... in.

Przykładowo, aby wyświetlić w kolejnych liniach wszystkie elementy sekwencji lista1 napiszemy:

 

>>> for a in lista1:

      print a

 

     

2

4

5

4

 

gdzie a jest przykładową zmienną, która w danym powtórzeniu pętli przyjmuje wartość kolejnych elementów sekwencji.

 

Aby poznać numer aktualnego powtórzenia pętli, posługujemy się funkcją enumerate:

 

>>> for nr, wartosc in enumerate(lista2):

      print nr,

print wartosc

 

     

0 pierwszy

1 ostatni

 

gdzie nr jest zmienną zawierająca aktualny numer obiegu pętli, a wartosc zmienną, która w danym powtórzeniu pętli przyjmuje wartość kolejnych elementów sekwencji.

 

Możemy wykonywać pętlę tylko dla fragmentu sekwencji:

 

>>> for i in lista7[1:3]:

      print i,i**2

 

     

4 16

5 25

 

A wewnątrz pętli wykonywać instrukcje warunkowe:

 

>>> for i in lista7:

      if i>0:

            print i, i**0.5

 

           

2 1.41421356237

4 2.0

5 2.2360679775

4 2.0

5.18    Ćwiczenia kontrolne

I.                    Napisz program "parzyste2.py", który wczyta od użytkownika liczbę całkowitą i bez użycia instrukcji if wyświetli informację, czy jest to liczba parzysta, czy nieparzysta.

II.                 Napisz program "numer.py", który zamieni wprowadzony przez użytkownika ciąg cyfr na formę tekstową:

a.       znaki nie będące cyframi mają być ignorowane

b.      konwertujemy cyfry, nie liczby, a zatem:

                                                               i.      911 to "dziewięć jeden jeden"

                                                             ii.      1100 to "jeden jeden zero zero"