JEZYK PROGRAMOWANIA PYTHON: SEKWENCJE (ŁAŃCUCHY, LISTY, KROTKI), METODY LIST E. Dyguda-Kazimierowicz 1 Sekwencje: indeksowanie i wycinanie Poznane dotąd obiekty łańcuchowe należą do typu danych złożonych są kolekcjami (zbiorami) znaków. Ponieważ ich cechą jest uporządkowanie, kolekcje te określa się mianem sekwencji. Uporządkowanie oznacza zdefiniowaną kolejność elementów tworzących daną sekwencję (np. znaków w łańcuchu). Każda pozycja jest jednoznacznie identyfikowana przez swój numer, czyli indeks. Numerowanie pozycji zaczyna się od zera, zatem indeks np. pozycji drugiej ma wartość 1, a ostatniej wartość o 1 mniejszą od długości danej sekwencji. Poprzez odwołanie się do indeksu, możliwe jest pobranie dowolnej pozycji z sekwencji (operacja indeksowania; indeks podawany jest w nawiasach kwadratowych bezpośrednio po nazwie zmiennej). Próba skorzystania z indeksu wykraczającego poza dozwolony zakres (np. indeks len(s) ) kończy się błędem. Użycie ujemnych indeksów oznacza odliczanie od końca : pozycja ostatnia ma indeks -1, przedostatnia to -2 (ujemne indeksy zamieniane są na dodatnie wartości poprzez dodawanie do długości sekwencji): >>> s = indeksy >>> print s[0], s[1], s[len(s)-1] i n y >>> s[len(s)] #!BLAD! Traceback (most recent call last): File "<stdin>", line 1, in? IndexError: string index out of range >>> print s[-1], s[-2], s[-len(s)] y s i >>> n = -5 >>> s[n] == s[len(s)+n] True 1
2 LISTY I KROTKI Operacja wycinania polega na pobraniu fragmentu sekwencji zawartego między dwoma indeksami (lewa i prawa wartość to odpowiednio dolna i górna granica, zwracany jest ciągły wycinek sekwencji zaczynający się od dolnej granicy i kończący pozycją położoną bezpośrednio przed górną granicą): >>> print s[0:3], s[-3:6] ind ks Pominięcie dolnej lub górnej granicy spowoduje przyjęcie domyślnych wartości: odpowiednio 0 lub długość sekwencji, len(s). Tym samym zakres [:] równoznaczny jest z pobraniem kopii całej wyjściowej sekwencji. W przypadku wycinania użycie indeksu wykraczającego poza zakres nie jest traktowane jako błąd takie wartości domyślnie zastępowane są zerem (indeks o zbyt małej wartości) lub długością sekwencji (indeks o zbyt dużej wartości). Jeśli prawy indeks wskazuje pozycję znajdującą się przed lewym, zwracany jest pusty łańcuch: >>> print s[:3], s[3:], s[:] ind eksy indeksy >>> print s[-10:10] indeksy >>> a, b = s[2:1], s[-1:-2] >>> print len(a), len(b) 0 0 2 Listy i krotki Listy (ang. list ) i krotki (ang. tuple ) to kolejne, po liczbach i łańcuchach, typy obiektów. Podobnie jak łańcuchy, są uporządkowanymi sekwencjami obiektów, jednak w przeciwieństwie do łańcuchów, listy i krotki mogą przechowywać obiekty dowolnego typu (łącznie z innymi listami czy krotkami). Tworzenie list/krotek polega na podaniu zestawu oddzielonych przecinkami obiektów zamkniętych w nawiasy kwadratowe/zwykłe: >>> lista1 = [3, napis, -1, x ] >>> krotka1 = ( pi, inny napis, 0.0, 100) >>> lista2 = [lista1, krotka1] #zagniezdzenie obiektow >>> krotka2 = ([2, 3], krotka1, lista1) 2 [[3, napis, -1, x ], ( pi, inny napis, 0.0, 100)] >>> print krotka2 2
3 OPERACJE NA LISTACH I KROTKACH ([2, 3], ( pi, inny napis, 0.0, 100), [3, napis, -1, x ]) >>> pusta_lista = []; pusta_krotka = () >>> print type(pusta_lista), type(pusta_krotka) <type list >, <type tuple > >>> print len(pusta_lista), len(pusta_krotka) 0, 0 #funkcja len() dziala na dowolnych sekwencjach >>> L = [1] #jednopozycyjna lista >>> K = (1,) #jednopozycyjna krotka >>> k = (1) #to nie jest krotka! >>> print type(k), type(k) <type tuple > <type int > 3 Operacje na listach i krotkach Ponieważ listy i krotki zaliczają się do tej samej, co łańcuchy, kategorii obiektów będących sekwencjami, można na nich przeprowadzać te same operacje (tzw. operacje sekwencyjne): indeksowanie, wycinanie, łączenie, powtarzanie (łączyć można wyłącznie obiekty tego samego typu!). 1[2] #indeksowanie -1 >>> print krotka1[1:] #wycinanie ( inny napis, 0.0, 100) 2[0][1] #indeksowanie zagniezdzonej sekwencji napis 2[0][1][-1] s 2[1][1:] #wycinanie zagniezdzonej sekwencji ( inny napis, 0.0, 100) >>> [2, 3] + [4, 5] + [2, abc ] #laczenie [2, 3, 4, 5, 2, abc ] >>> krotka1 + ( jeszcze to,) ( pi, inny napis, 0.0, 100, jeszcze to ) >>> (1,)*10 #powtarzanie (1, 1, 1, 1, 1, 1, 1, 1, 1, 1) >>> (1, [2, 3])*5 (1, [2, 3], 1, [2, 3], 1, [2, 3], 1, [2, 3], 1, [2, 3]) 3
3 OPERACJE NA LISTACH I KROTKACH Przekształcenia sekwencji na listę lub krotkę dokonać można za pomocą odpowiednio funkcji list() albo tuple(). Funkcja str() zamienia dowolny obiekt na łańcuch, jednak wyrażenie str(list( jakis napis )) nie zwraca wyjściowego łańcucha jakis napis (patrz poniższe przykłady). Do tego celu służy metoda łańcuchowa join(), która łączy pozycje listy (ewentualnie krotki lub łańcucha) podanej jako argument i zwraca łańcuch (lista bądź krotka zawierać muszą wyłącznie obiekty typu łańcuchowego). Poszczególne pozycje listy rozdzielane są tzw. ogranicznikiem podawanym jako obiekt, na który działa metoda. Odwrotny efekt daje metoda split() pozwala ona na podział łańcucha we wskazanych miejscach z utworzeniem listy fragmentów. Jeżeli argument będacy ogranicznikiem zostanie pominięty, podział następuje domyślnie w miejscach występowania odstępów (spacji, znaków tabulacji, itd.): >>> list( napis->lista ) [ n, a, p, i, s, -, >, l, i, s, t, a ] >>> tuple( napis->krotka ) ( n, a, p, i, s, -, >, k, r, o, t, k, a ) >>> list(krotka1) [ pi, inny napis, 0.0, 100] >>> tuple(lista1) (3, napis, -1, x ) >>> str(list( napis->lista )) #przeksztalcenie na lancuch "[ n, a, p, i, s, -, >, l, i, s, t, a ]" >>>.join(list( napis->lista )) napis->lista >>> _.join(( aa, bb, cc, dd )) aa_bb_cc_dd >>> aa_bb_cc_dd.split( _ ) [ aa, bb, cc, dd ] >>> aa bb cc dd.split() [ aa, bb, cc, dd ] Operatory relacyjne znajdują zastosowanie również w przypadku list i krotek: porównywane są kolejne, odpowiadające sobie pozycje; prawdziwość porównania ustalana jest na podstawie pierwszej napotkanej różnicy (niezależnie od dalszej zawartości listy/krotki). Reguły dla wyrażeń i wartości logicznych pozostają niezmienione: pusta lista/krotka ma wartość logiczną fałsz, obiekty różnych typów są zawsze różne, sposób porównywania pozycji uzależniony jest od typu przechowywanego obiektu. Za pomocą operatora zawierania in można określić przynależność danego obiektu do sekwencji: 4
4 ZMIENNOŚĆ I NIEZMIENNOŚĆ SEKWENCJI >>> [2, 3, -1] < [2, 3, 1] True >>> ( xyz, 1, 2, a ) > ( xyz, 1, 2, A ) True >>> () or [] or >>> (1, 2) == [1, 2] False >>> 2 in ( xyz, 1, 2, a ) True >>> x in ( xyz, 1, 2, a )[0] True 4 Zmienność i niezmienność sekwencji Różnica między listami i krotkami polega na tym, że listy są zmienne, natomiast krotki (a także łańcuchy) należą do obiektów niezmiennych. Obiekty niezmienne nie mogą być modyfikowane w miejscu, np. poprzez przypisanie do indeksu. Aby zmienić łańcuch albo krotkę, trzeba utworzyć je na nowo można do tego wykorzystać kopię obiektu wyjściowego lub jego wycinka: >>> napis = obiekt niezmienny >>> napis[0] = O #!BLAD! >>> napis = napis[0].upper() + napis[1:] #OK >>> print napis Obiekt niezmienny >>> krotka = (1, a, 2, b ) >>> krotka[2:] = ( b, c ) #!BLAD! >>> krotka = krotka[:2] + ( b, c ) #OK >>> print krotka (1, a, b, c ) W przeciwieństwie do krotek i łańcuchów, listy mogą być w dowolny sposób modyfikowane bez tworzenia ich kopii (modyfikacja w miejscu). Dotyczy to zarówno zmiany pojedynczych pozycji w liście, jak i zmiany rozmiaru całej listy, tzn. usuwania bądź dodawania pozycji, także wewnątrz listy (rozmiar łańcuchów i krotek jest ustalony i nie podlega zmianom). Ponieważ odwołanie się do nieistniejącego 5
4 ZMIENNOŚĆ I NIEZMIENNOŚĆ SEKWENCJI indeksu tutaj również jest błędem, aby dopisać pozycję na końcu listy należy posłużyć się zakresem, np. [len(lista):]: >>> lista = [1, a, 2, b ] >>> lista[0] = jeden #zmiana zawartości pozycji [ jeden, a, 2, b ] >>> lista[2:] = [ b, c ] #zamiana fragmentu [ jeden, a, b, c ] >>> lista[1:3] = [] #usuwanie pozycji [ jeden, c ] >>> lista[1:1] = [ A, B, C, D ] #wstawianie pozycji [ jeden, A, B, C, D, c ] >>> lista[6] = E #!BLAD! >>> lista[6:] = E #OK, dopisanie pozycji na koncu [ jeden, A, B, C, D, c, E ] W powyższych przykładach przypisanie pustej listy do segmentu powoduje jego usunięcie. Takie samo przypisanie do indeksu wstawi pustą listę w pozycji określonej indeksem. Ponadto przypisanie do segmentu możliwe jest tylko w przypadku sekwencji próba przypisania obiektu nie będącego sekwencją kończy się komunikatem o błędzie: >>> lista[1:] = 2 #!BLAD! >>> lista[1:] = 2, #liczba 2 jako jednopozycyjna krotka [ jeden, 2] Podczas przypisywania zmiennej wartości już istniejącego obiektu, w rzeczywistości kopiowana jest nie sama wartość, a odwołanie do niej. O takim obiekcie mówi się, iż jest on utożsamiany lub współdzielony, natomiast obie zmienne odwołujące się do tego obiektu są jego aliasami. W przypadku obiektów niezmiennych, przypisanie nowej wartości pierwszej zmiennej nie powoduje takiej samej zmiany drugiej: wartość pierwotna jest kopiowana, a powiązanie z pierwszą zmienną utracone. Dzieje się tak dlatego, że zmiana wartości zmiennej przechowującej obiekt niezmienny możliwa jest tylko w wyniku utworzenia nowego odwołania: 6
6 METODY LIST >>> krotka_1 = (1, 2, 3, xyz ) >>> krotka_2 = krotka_1 >>> print krotka_1, krotka_2 (1, 2, 3, xyz ) (1, 2, 3, xyz ) >>> krotka_1 = ( abc,) >>> print krotka_1, krotka_2 ( abc,) (1, 2, 3, xyz ) Tworzenie odwołań do tego samego obiektu zmiennego (np. listy) działa inaczej: ponieważ utożsamiany obiekt może być modyfikowany w miejscu bez konieczności tworzenia nowego, zmiana wprowadzona w jednym aliasie będzie widoczna w kolejnych: >>> lista_1 = [1, 2, 3, xyz ] >>> lista_2 = lista_1 _1, lista_2 [1, 2, 3, xyz ] [1, 2, 3, xyz ] >>> lista_1[0] = abc _1, lista_2 [ abc, 2, 3, xyz ] [ abc, 2, 3, xyz ] 5 Użycie krotek i list Ze względu na swoją niezmienność, krotki zachowują integralność danych ich niejawne zmodyfikowanie poprzez alias nie jest możliwe. Niektóre operacje i funkcje niejako wymuszają stosowanie krotek (np. podczas formatowania łańcucha, operand znajdujący się po prawej stronie operatora % musi być krotką). Ponieważ krotki są wydajniejsze (działają szybciej i zajmują mniej miejsca w pamięci), jeśli nie ma konieczności modyfikowania danej kolekcji obiektów, zaleca sie ich użycie zamiast list. 6 Metody list Działanie poznanych ostatnio metod łańcuchowych polega na pobraniu zawartości danego napisu i zwrócenie nowego łańcucha będącego wynikiem określonego przekształcenia. Aby zachować wartość będącą wynikiem działania metody łańcuchowej, trzeba przypisać ją zmiennej. Metody modyfikujące listy działają w inny sposób (na zasadzie skutków ubocznych): zmiana dokonywana jest w miejscu, czyli na wyjściowym egzemplarzu listy; zamiast nowej listy zwracany jest obiekt pusty None. 7
6 METODY LIST Tym samym metody list stosuje się jako wyrażenia będace instrukcjami, czyli poprzez wywołanie nie połączone z jednoczesnym przypisaniem jego wyniku zmiennej: >>> liczby = [3, 5, -2, 121, 77] >>> liczby.sort() #sortowanie pozycji listy >>> print liczby [-2, 3, 5, 77, 121] >>> type(liczby.sort()) <type NoneType > >>> liczby = liczby.sort() #powoduje utrate pierwotnej listy! >>> print liczby None Kilka metod działających na listach na zasadzie ich modyfikowania w miejscu wymieniono poniżej (wszystkie zwracają obiekt pusty None): dołączanie pozycji do listy metody append() i extend() dołączają na końcu listy odpowiednio pojedynczy obiekt lub kilka obiektów podanych jako sekwencję; metoda insert() pozwala wstawić pojedynczy obiekt w określonej pozycji: >>> lista = [ a, b ] >>> dolacz = ( d, e, f ) >>> lista.append( c ) [ a, b, c ] >>> lista.extend(dolacz) [ a, b, c, d, e, f ] >>> lista.extend( gh ) [ a, b, c, d, e, f, g, h ] >>> lista.insert(0, X ) #wstawienie X w pozycji 0 [ X, a, b, c, d, e, f, g, h ] usuwanie pozycji z listy instrukcja del usuwa pozycję o zadanym indeksie (lub kilka pozycji objętych zakresem), metoda remove() usuwa pierwszą napotkaną pozycję o zadanej wartości: 8
6 METODY LIST >>> del lista[len(lista)/2-2:len(lista)/2+2] [ X, a, f, g, h ] >>> lista.remove( X ) [ a, f, g, h ] sortowanie listy zaprezentowana powyżej metoda sort() służy do sortowania w miejscu w kolejności rosnącej, metoda reverse() odwraca kolejność elementów listy: >>> lista = [ x, a, q, c ] >>> lista.sort() [ a, c, q, x ] >>> lista.reverse() [ x, q, c, a ] Do metod zwracających użyteczne wartości (zamiast obiektu None) należy metoda pop() (zwraca ostatnią pozycję listy i jednocześnie usuwa ją z listy), count() (zlicza wystąpienia danego obiektu w liście) oraz index() (zwraca indeks pierwszego wystąpienia obiektu w liście). Dwie ostatnie z wymienionych tu metod nie zmieniają listy, na którą działają, natomiast metoda pop(), oprócz zwracania wartości różnej od None, modyfikuje wyjściową listę w miejscu: >>> ostatnia_pozycja = lista.pop(), Zdjeto:, ostatnia_pozycja [ x, q, c ] Zdjeto: a >>> kolejna_pozycja = lista.pop(), Zdjeto:, kolejna_pozycja [ x, q ] Zdjeto: c >>> nowa_lista = [1, 2, 1, 3, 4, 1] >>> x = nowa_lista.count(szukaj) >>> y = nowa_lista.index(szukaj) 9
6 METODY LIST >>> print W liscie %s liczba %d wystepuje %d razy,... po raz pierwszy pojawia sie na pozycji %d % (... nowa_lista, szukaj, x, y) W liscie [1, 2, 1, 3, 4, 1] liczba 1 wystepuje 3 razy, po raz pierwszy pojawia sie na pozycji 0 Informacje o metodach działających na listy znaleźć można w samych listach (dir([])). Analogicznie wyświetlić można atrybuty krotek: dir(()). Warto zwrócić uwagę na fakt, że krotki nie mają metod. 10