W tym „odcinku” tutoriala poznamy pewne „różności”, które mogły nam umknąć poprzednio i świetnie wypełnią braki, jakie możemy mieć w wiedzy, zanim ruszymy do bardziej zaawansowanych tematów.
Jakiegoś tematu przewodniego tego odcinka nie ma, po prostu wypełniamy braki w wiedzy.
Zadanie – podaj ilość unikalnych elementów
Mamy za zadanie podać ilość unikalnych elementów, występujących, niech będzie w liście. Powinniśmy umieć sobie z tym poradzić bez problemu. Oto jedno podejście:
def unique_elements(lst):
unique_lst = []
for el in lst:
if el not in unique_lst:
unique_lst.append(el)
return len(unique_lst)
print(unique_elements([1,1,2,2,3,3,3]))
#3
print(unique_elements([1,2,3,4,4]))
#4
Tutaj przyjmujemy listę, tworzymy drugą, pustą. Iterujemy po pierwszej i tylko jeżeli elementu takiego nie mamy (not in) w unique_list, taki element do niej dodajemy. Zwracamy długość listy zawierającej tylko unikalne wartości.
Set, czyli zbiór
Mamy taki typ danych jak zbiór, który może nam pomóc rozwiązać takie zadanie szybciej, także z komputerowego punktu widzenia. Set, czyli zbiór, to taka lista unikalnych wartości. Tworzymy go tak:
msg = "hello"
msg_set = set(msg)
print(msg_set)
#{'e', 'o', 'h', 'l'}
Jak widać, podobne toto do słownika, jeżeli o literał idzie (znak {}) ale w słowniku są pary klucz, wartość. A to taki zbiór, ale elementy nie mogą się powtarzać.
A gdybyśmy tak starali się „na sztywno” stworzyć zbiór z duplikatami?
msg_set = {"h", "e", "l", "l", "o"}
print(msg_set)
#{'h', 'e', 'o', 'l'}
Nie działa. Podobnie jak dodawanie czegoś, co już jest:
msg_set = {"h", "e", "l", "l", "o"}
print(msg_set)
#{'l', 'e', 'o', 'h'}
msg_set.add("h")
print(msg_set)
#{'l', 'e', 'o', 'h'}
Nie działa. Ciekawym jest to, że te elementy nie są uporządkowane, występują w różnych kolejnościach. Jak widać zachowanie kolejności nie jest priorytetem w zbiorze, jedynie stworzenie kolekcji unikalnych, niepowtarzających się danych. Stąd możemy wysnuć (słuszny) wniosek, że zbiór działa szybciej niż lista. I jeżeli listę możemy zastąpić zbiorem, to powinniśmy.
Spróbujmy napisać funkcję, która zwraca liczbę unikalnych elementów, używając zbioru:
def unique_elements(lst):
return len(set(lst))
print(unique_elements([1,1,2,2,3,3,3]))
#3
print(unique_elements([1,2,3,4,4]))
#4
To takie proste.
Literały – lista, zbiór, słownik, tupla
Zacznijmy od stworzenia listy, zbioru, słownika i tupli przy pomocy funkcji, nie literałów.
my_list = list("abc")
my_set = set("aabbcc")
my_dict = dict(name="John", age=30)
my_tuple = tuple([11,22])
print(my_list)
print(my_set)
print(my_dict)
print(my_tuple)
# ['a', 'b', 'c']
# {'b', 'c', 'a'}
# {'name': 'John', 'age': 30}
# (11, 22)
Lista to sekwencja uporządkowanych danych, jej literał zawiera nawiasy []. Zbiór (set) to nieuporządkowany zbiór unikalnych danych, jego literał zawiera nawiasy {} z elementami po przecinku wypisanymi. Słownik to zbiór par klucz-wartość, jego literał zawiera nawiasy { } oraz pary klucz : wartość, oddzielone dwukropkiem oraz przecinkiem od innych par. Tupla to niemutowalna lista, nawias tradycyjny ( ) oraz elementy po przecinku.
Tak wygląda tworzenie tych typów danych z użyciem literałów:
my_list = ["a", "b", "c"]
my_set = {"a", "b", "c"}
my_dict = {"name": "John", "age": 30}
my_tuple = (11,22)
print(type(my_list))
print(type(my_set))
print(type(my_dict))
print(type(my_tuple))
# <class 'list'>
# <class 'set'>
# <class 'dict'>
# <class 'tuple'>
W przypadku pustych literałów, możemy się zastanawiać, czym jest {} – pustym słownikiem a może pustym zbiorem?
print(type({}))
# <class 'dict'>
Pusty słownik, co nie powinno dziwić, słowniki są używane częściej niż zbiory. Problematyczna jest także tupla jednoelementowa:
print(type((10)))
# <class 'int'>
Jak widać sam nawias nie czyni tuplą (krotką, listą niemutowalną). Za to przecinek tak, nawet bez nawiasów:
tpl1 = 10,
tpl2 = (10,)
print(type(tpl1), type(tpl2))
# <class 'tuple'> <class 'tuple'>
Tworzenie pustych typów danych:
my_list = []
my_set = set()
my_dict = {}
my_tuple = tuple()
print(type(my_list))
print(type(my_set))
print(type(my_dict))
print(type(my_tuple))
# <class 'list'>
# <class 'set'>
# <class 'dict'>
# <class 'tuple'>
Comprehensions – list, set, dict
Comprehensions czyli składanie to bardzo potężne narzędzie, dostępne zarówno dla list, zbiorów jak i słowników. Wystarczy znać literały tychże i zrozumieć ogólną koncepcję, i już można się nimi posługiwać.
Oto przykład list comprehension:
my_list = [num ** 2 for num in range(1,6)]
print(my_list)
#[1, 4, 9, 16, 25]
Bierzemy liczby od 1 do 5 (bez 6) i w tym zakresie wrzucamy je podniesione do kwadratu.
Przykład dictionary comprehension:
my_dict= {num: num ** 2 for num in range(1,6)}
print(my_dict)
#{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Posługując się odpowiednim literałem bierzemy liczby w zakresie 1 do 5 (bez 6) i jako klucza używamy tej liczby zaś jako wartości, tej liczby podniesionej do kwadratu.
Oto przykład set comprehension:
my_set = {num ** 2 for num in range(1,6)}
print(my_set)
#{1, 4, 9, 16, 25}
Posługując się odpowiednim literałem (podobnym do słownika, ale jednak innym, tutaj nie mamy par klucz wartość dzielonych dwukropkiem) wrzucamy do tego zbioru liczby od 1 do 5 podniesione do kwadratu. Nie muszą być w kolejności, zbiór tego nie zachowuje. Natomiast zbiór to kolekcja unikalnych elementów. Oto bardziej praktyczny przykład składania zbioru:
msg = "Hello World"
my_set = {char.lower() for char in msg}
print(my_set)
# {'o', 'l', 'w', 'r', 'h', ' ', 'd', 'e'}
Do zbioru zapisujemy, małą literą, każdy znak z napisu msg. Kolejność nie jest zachowana, natomiast duplikaty nie wchodzą.
Możemy jeszcze dołączyć filtrowanie i zabronić wejścia spacjom:
msg = "Hello World"
my_set = {char.lower() for char in msg if char != " "}
print(my_set)
# {'d', 'w', 'e', 'h', 'o', 'l', 'r'}
Możemy też jeszcze bardziej dopracować warunek, aby tylko znaki będące literami alfabetu do tego zbioru wchodziły:
msg = "123 Hello World!!!"
my_set = {char.lower() for char in msg if char.isalpha()}
print(my_set)
# {'e', 'l', 'o', 'w', 'd', 'r', 'h'}
Indeksy i wycinki
Element pierwszy ma zawsze indeks zerowy, zaś element ostatni to długość minus 1, albo -1 w zapisie skróconym:
msg = "Hello World"
print(msg[0])
print(msg[len(msg) -1])
print(msg[-1])
# H
# d
# d
Element przedostatni to długość -2 albo w zapisie skróconym -2:
msg = "Hello World"
print(msg[len(msg) -2])
print(msg[-2])
# l
# l
Listy i tuple też podlegają indeksowaniu:
my_lst = [1,2,3]
my_tuple = (22,33)
print(my_lst[0], my_lst[-1])
print(my_tuple[0], my_tuple[-1])
# 1 3
# 22 33
Możemy też tworzyć wycinki od-do gdzie pierwszy argument to odkąd, drugi to dokąd, ale już bez niego:
msg = "Hello World"
print(msg[0:5])
#Hello
print(msg[:5])
#Hello
Czyli [0:5] lub w zapisie skróconym [:5] to wycinek od indeksu 0 włącznie, do indeksu 5, bez indeksu 5 (tam akurat mamy spację).
Kolejny przykład wycinka:
msg = "Hello World"
print(msg[6:])
#World
Tutaj idziemy od indeksu 6 włącznie aż do samego końca. Tutaj inny przykład:
msg = "Hello World!"
print(msg[6:-1])
#World
[6:-1] czyli od indeksu 6 włącznie do ostatniego znaku, bez ostatniego znaku.
Warto jeszcze znać sposób na odwracanie napisów/list:
msg = "Hello World!"
print(msg[::-1])
#!dlroW olleH
A także notację służącą do kopiowania list:
lst1 = [1,2,3]
lst2 = lst1[:]
print(lst1, lst2)
print(lst1 == lst2)
print(lst1 is lst2)
# [1, 2, 3] [1, 2, 3]
# True
# False
Zamienianie zmiennych zawartością
Kolejny element składni Pythona, który w zasadzie komentuje się sam, ale trzeba znać ten zapis. Zamieniamy zmienne zawartością:
num1 = 5
num2 = 10
print(num1, num2)
# 5 10
num1, num2 = num2, num1
print(num1, num2)
# 10 5
Więcej elementów składni Pythona poznamy w następnych odcinkach tutoriala.