Wprowadzenie
Ostatnio zostałem zapytany o różnicę między tuplą i listą. Przyznam szczerze, że trochę zbiło mnie to z rytmu, bo nie byłem w stanie z marszu opowiedzieć wszystkich różnic. Oczywiście wiedziałem, że obie struktury istnieją. Używałem obu podczas codziennej pracy i to z poprawnym ich przeznaczeniem. Znałem z grubsza różnicę ale nie potrafiłem ich dokładnie określić. Dlatego też postanowiłem napisać ten wpis, żeby w przyszłości inne osoby, które się z tym spotkają nie miały już tego problemu. Dla wielu osób spora część tekstu może być już znana ale zachęcam do przeczytania, bo może dowiecie się czegoś ciekawego? ;).
Zanim odpowiemy na pytanie czym różni się lista od tupli musimy zapoznać się z każdą z tych struktur z osobna. Zacznijmy więc od tupli.
Tuple w Python
Tuple, w zasadzie po polsku nazywane krotkami to niezmienny typ danych (ang. immutable). Co to właściwie znaczy? W dużym uproszczeniu można powiedzieć, że można z niej pobierać dane ale nie można ich modyfikować ani usuwać. Powinna być zatem używana wszędzie tam, gdzie dane muszą być stałe. Tuple możemy rozróżnić po tym, że podczas jej tworzenia wykorzystujemy standardowe, okrągłe nawiasy () .
names = ('ddeby', 'listy', 'tuple')
W tupli dane można rozpakowywać:
a, b, c = names
W krotce można się również odwoływać bezpośrednio po indeksach:
names[0] # 'ddeby'
Można również wycinać fragmenty krotki:
names[1:] # 'listy', 'tuple'
Ostatecznie można też usunąć całą tuplę. Ale tylko całą:
del names
Listy w Python
Podstawową różnicą w stosunku do krotki, jest fakt iż listy są mutowalne, czyli w przeciwieństwie do poprzedniej struktury tu możemy dodawać nowe elementy i edytować, bądź usuwać istniejące. Dla rozróżnienia, do jej deklaracji używa się kwadratowych nawiasów [] .
names = ['ddeby', 'listy', 'tuple']
Listy można rozpakowywać:
a, b, c = names
Na liście można się również odwoływać bezpośrednio po indeksach:
names[0] # 'ddeby'
Z listy można wycinać fragmenty danych:
names[1:] # 'listy', 'tuple'
Jak widzimy lista ma dokładnie te same możliwości co tupla. Jest jednak nieco bardziej rozbudowana.
Lista jest mutowalna
Jak pisałem wcześniej lista jest mutowalna. A to oznacza, że dane można edytować.
Możemy na przykład do listy dodawać nowe elementy:
names = ['ddeby', 'listy', 'tuple']
names.append('krotki')
names # ['ddeby', 'listy', 'tuple', 'krotki']
Można również edytować konkretny element:
names[1] = 'LISTY'
names # ['ddeby', 'LISTY', 'tuple', 'krotki']
Spróbujmy jeszcze usunąć element z listy:
del names[0]
names # ['LISTY', 'tuple', 'krotki']
Możemy też pójść dalej i usunąć fragment listy:
del names[1:]
names # ['LISTY']
Tupla jest niemutowalna
Spróbujemy wykonać te same operacje co wcześniej na tupli. To pokaże nam co znaczy, że tupla jest niemutowalna.
Dodanie elementu:
names.append('LISTY') # AttributeError: 'tuple' object has no attribute 'append'
Edycja elementu:
names[1] = 'TUPLE' # TypeError: 'tuple' object does not support item assignment
Usuwanie elementu:
del names[0] # TypeError: 'tuple' object doesn't support item deletion
Funkcje
Można już zauważyć, że lista jest niejako rozszerzeniem tupli. Dlatego właśnie podstawowe funkcje dostępne w języku Python działają na obu tych strukturach. Są to min. len(), max(), min(), sum(), any(), all(), sorted() . Na przykład:
my_list = [1, 2, 4, 3, 7, 8, 9]
my_tuple = (1, 2, 4, 3, 7, 8, 9)
len(my_list) # 7
len(my_tuple) # 7
Metody
Idąc dalej tym tropem sprawdźmy jakie metody dostępne są w tupli a jakie na liście.
Tupla ma dostępne dwie publiczne metody i są to
count()
, która zwraca ile razy występuje element w zbiorcze oraz
index()
, informująca o pozycji występowania elementu.
data = (1, 2, 2, 3, 3, 4, 3, 4, 4, 4)
data.count(9) # 0
data.count(3) # 3
data.index(4) # 5
Lista z kolei posiada znacznie więcej tych metod i są to głównie metody odpowiedzialne za modyfikację danych. Są to: append(), clear(), copy(), extend(), index(), insert(), pop(), remove(), reverse(), sort() na przykład:
my_list = [1, 2, 5, 3, 7, 4]
my_list.sort()
my_list # [1, 2, 3, 4, 5, 7]
my_list.pop(5) # 7
my_list # [1, 2, 3, 4, 5]
Podsumowanie:
Myślę, że teraz możemy sobie odpowiedzieć czym różnią się listy od tupli w Python. Podstawową różnicą jest fakt, że listy w przeciwieństwie do tupli są mutowalne. I to właśnie jest nasz punkt wyjścia. Listy bowiem dają znacznie więcej możliwości, przez edycję elementów. Tuple natomiast są bezpieczniejsze, bo mamy pewność, że dane nie zostaną zmodyfikowane. Dodatkowo z tupli można zbudować hash, a to bardzo ciekawa informacja, bo dzięki temu tuple można podać jako klucz w słowniku, a to informacja która nie jest oczywista.
names = ('ddeby', 'Dawid')
data = {names: 'developers'} # data = {names: 'developers'}
Bonus:
Na koniec chciałbym podać jeszcze jeden ciekawy błąd z wykorzystaniem listy. Szerzej opiszę go w innym wpisie.
Czy poniższy kod jest poprawny? Albo czy widzicie tym kodzie jakieś błędy?
def foo(data=[]):
data.append('bar')
return data
Teoretycznie wszystko jest OK. Pod warunkiem, że zawsze będziemy przekazywać parametr przy wywołaniu. Jeśli tego nie zrobimy ta lista z wartości domyślnej będzie współdzielona podczas życia programu, co możemy zauważyć na poniższym przykładzie:
foo() # ['bar']
foo() # ['bar', bar']
foo() # ['bar', bar', bar']
Niżej przykład jak zrobić to nieco lepiej:
def foo(data=None):
data = [] if data is None else data
data.append('bar')
return data
foo() # ['bar']
foo() # ['bar']
foo() # ['bar']
Macie pomysł na inne ciekawe tematy, które warto poruszyć?