Commit 4769b4b2 by Patryk Czarnik

teoria kolekcji - ciąg dalszy

parent 77f11fe4
# Słowniki służą do przechowywania par klucz -> wartość
# Umożliwiają łatwy i szybki dostęp do elementów po ich kluczu
# Są mutowalne (można zmieniać zawartość)
# Klucze nie mogą się powtarzać.
# Zalecane jest, aby klucze były wartościami, które łatwo jest porównywać, i które są "niemutowalne".
# Najczęściej kluczami są napisy, ale poprawne byłyby też np. int-y
slownik = {'Ala': 30, 'Ola': 40, 'Ela': 40}
print(slownik)
print(slownik['Ala'])
slownik['Ala'] += 1
print(slownik)
# dodanie nowego elementu do słownika wygląda po prostu tak:
slownik['Ula'] = 25
print(slownik['Ula'])
print(slownik)
# aktualizacja istniejącej wartości wygląda dokładnie tak samo
slownik['Ula'] = 27
print(slownik['Ula'])
print(slownik)
kto = 'Ala'
wiek = slownik[kto]
print(f'Osoba {kto} ma {wiek} lat')
print()
# Można to wykorzystać np. tak:
for kto in ['Ala', 'Ola']:
print(f'Osoba {kto} ma {slownik[kto]} lat')
print()
# Pusty słownik
pusty_slownik_1 = {}
pusty_slownik_2 = dict()
print(pusty_slownik_1, type(pusty_slownik_1))
print(pusty_slownik_2, type(pusty_slownik_2))
print()
cennik = {'pralka': 1900, 'czajnik': 280, 'odkurzacz': 280}
print(cennik['odkurzacz'])
# kluczami nie muszą być zawsze stringi - może być coś innego, np. liczby
dni_tygodnia = {
1: "poniedziałek",
2: "wtorek",
3: "środa",
4: "czwartek",
5: "piątek",
6: "sobota",
7: "niedziela",
}
print(cennik)
print(dni_tygodnia)
print(len(dni_tygodnia))
print(dni_tygodnia[6])
print()
# Słownik jest zoptymalizowany pod kątem szybkiego i wygodnego dostępu do danych poprzez klucz.
# Klucze powinny być obiektami porównywalnymi (mieć dobrze zdefiniowane operacje eq i hash)
# i niemutowalymi (stan klucza nie może się zmieniać w trakcie życia słownika, bo inaczej nie znajdziemy elementów które wcześniej wstawiliśmy)
# W praktyce najczęściej kluczami są napisy (str), czasami liczby całkowite (ale nie floaty),
# daty, mogą być też tuple złożone z takich typów lub nasze własne klasy po odpowiednim przygotowaniu.
# Wartości mogą być dowolnych typów.
# Czasami są to pojedyncze wartości (liczby, napisy), czasami rozbudowane obiekty (Towar, Faktura), a czasami inne kolekcje (np. listy).
szkolenia_w_miastach = {
'Warszawa': ['Java', 'Python', 'SQL', 'Excel'],
'Wrocław': ['Python'],
'Kraków': ['Java', 'PHP'],
'Poznań': [],
}
szkolenia_w_miastach['Wrocław'].append('C#')
print(szkolenia_w_miastach)
# Sprawdzanie czy podany klucz jest zdefiniowany w słowniku:
# wygodne i wydajne
print('pomidor' in cennik) # False
print('czajnik' in cennik) # True
# Sprawdzenie czy podana wartość należy do wartości słownika można zrobić tak,
# ale trzeba sobie zdawać sprawę, że to jest mało wydajne (Python musi w pętli przejrzeć całą kolekcję)
print(1900 in cennik.values())
print()
# Odczyt elementu wg podanego klucza:
# 1) indeksowanie - składnia przypominająca korzystanie z list
print('Odkurzacz kosztuje', cennik['odkurzacz'])
cennik['odkurzacz'] = 333
cennik['odkurzacz'] += 2
print('Odkurzacz kosztuje', cennik['odkurzacz'])
# przy okazji: gdy w f-stringu chcemu odczytać coś z klucza tekstowego, to przydają się dwa rodzaje cudzysłowów
print(f'czajnik kosztuje {cennik["czajnik"]}')
# Jeśli próbujemy odczytać coś z klucza, który nie był wpisywany, to dojdzie do wyjątku KeyError
#ERR print(cennik['pomidor'])
print()
# Wyjaśnienie: pętla for i lista w tym przykładzie jest tylko po to,
# aby poniższą operację wykonać dwa razy: raz dla pralki, która instnieje, a drugi raz dla pomidora, który nie istnieje w cenniku
for towar in ['pralka', 'pomidor']:
if towar in cennik:
print(f'{towar} kosztuje {cennik[towar]}')
else:
print(f'Towaru {towar} nie ma w cenniku')
print()
# Inny zapis, podejście "ofensywne" - próbujemy odczytać, a w razie błedu obsługujemy wyjątek
for towar in ['pralka', 'pomidor']:
try:
print(f'Produkt {towar} ma cenę {cennik[towar]}')
except KeyError:
print(f'Produktu {towar} nie ma w cenniku')
print()
# ten sam zapis: slownik[klucz] = nowa_wartosc
# może być użyty zarówno do wpisania nowej wartości (pod nowy klucz)
# jak i do nadpisania istniejącej wartości (gdy klucz już istniał)
print(cennik)
cennik['kuchenka'] = 2499
print(cennik)
cennik['pralka'] = 1813
print(cennik)
print()
# print(cennik['pomidor']) - bład KeyKerror
# 2) metoda get
# działa tak jak odczyt za pomocą []
# ale jeśli danego klucza nie ma w słowniku, to zwraca None, a nie wyjątek
print(cennik.get('pralka')) # 1813
print(cennik.get('pomidor')) # None
# Można też podać wartość domyślną, która ma być użyta w przypadku braku danych
print(cennik.get('pralka', 0)) # 1813
print(cennik.get('pomidor', 0)) # 0
print()
for towar in ['pralka', 'pomidor']:
print(f'Pod kluczem {towar} jest wartość {cennik.get(towar)}')
print()
# Jak przejrzeć zawartość całego słownika?
# Domyślnie pętla for dla słownika przegląda wszystkie klucze
for towar in cennik:
print(towar)
print()
# Jak dostać się do wartości pod kluczami?
# 1) wersja mniej wydajna: za każdym razem odczytaj spod klucza:
for towar in cennik:
print(towar, cennik[towar])
print()
# 2) wersja bardziej wydajna: przeglądami wpisy jako pary
# for k, v in slownik.items():
# print('klucz:', k, 'wartość:', v)
print(cennik.items())
for item in cennik.items():
print(item)
print()
# Dzięki technice unpacking można te dwie rzeczy od razu wpisać do zmiennych
for towar, cena in cennik.items():
print(towar, cena)
# lub zupełnie inne operacje, które chcemy dla każdego elementu słownika
print()
# W sumie istnieją trzy podobne metody, które ze słownika tworzą "kolekcję czegoś"
# items() zwraca kolekcję par (klucz, wartość)
# keys() zwraca same klucze
# values() zwraca same wartości
for towar in cennik.keys():
print('Istnieje towar', towar)
print()
for cena in cennik.values():
print('Jakiś towar ma cenę', cena)
print()
# Poza wykorzystaniem w pętli for, można też w ten sposób utworzyć kolekcję samych kluczy lub samych wartości:
lista_towarow = list(cennik.keys())
print(lista_towarow)
posortowane_dni_tygodnia = list(dni_tygodnia.values())
posortowane_dni_tygodnia.sort()
print(posortowane_dni_tygodnia)
print()
# Można wyróżnić trzy najważniejsze zastosowania słowników w Pythonie:
# 1) "baza danych w pamięci"
# po prostu pamiętamy zestaw danych w taki sposób, aby łatwo odczytywać dane wg klucza
cennik = {'pomidor': 4.50, 'marchewka': 2.90}
# Jeśli dodatkowo mamy klasę albo coś zbliżonego do klasy,
# to wartościami mogą być obiekty
from collections import namedtuple
Towar = namedtuple('Towar', ['nazwa', 'cena', 'vat', 'stan'])
cennik2 = {
'pral': Towar('Pralka Amica', 2800.99, 0.23, 13),
'lod': Towar('Lodówka Electrolux', 3333.0, 0.23, 10),
'czaj': Towar('Czajnik Philips', 300, 0.08, 50),
}
print('Dane lodówki:', cennik2['lod'])
print('Cena lodówki:', cennik2['lod'].cena)
print()
# 2) "namiastka obiektu" - takich rzeczy ze słownikami nie robi się np. w Javie,
# ale w Pythonie bardzo często, m.in. gdy obsługuje się format JSON
towar = {'nazwa': 'pralka', 'cena': 2199.00, 'vat': 0.23, 'stan': 100}
waluta = {'kod': 'USD',
'nazwa': 'dolar amerykański',
'kurs': 3.80,
}
# 3) realizacja pewnego typu algorytmów, gdy dokonujemy "grupowania" danych (jak GROUP BY w SQL)
# Zobaczymy w przykładach o employeesach
miasta = ['Warszawa', 'Kraków', 'Łódź', 'Wrocław', 'Poznań']
print(miasta)
# list comprehension - jedolinijkowe utworzenie nowej listy na podstawie innej kolekcji (lub innego źródła danych)
dlugosci_nazw = [len(miasto) for miasto in miasta]
print(dlugosci_nazw)
# to jest równoważne takiej pętli:
dlugosci_nazw2 = []
for miasto in miasta:
dlugosci_nazw2.append(len(miasto))
print(dlugosci_nazw2)
print()
# Stwórz listę, która zawiera pierwsze litery każdego miasta
pierwsze_litery = [miasto[0] for miasto in miasta]
print(pierwsze_litery)
print()
# Można używać wiele razy for i if
# Najczęściej wyrażenia są pisane jednolinijkowo, ale gdyby była potrzeba, można dowolnie sformatować, bo jesteśmy w obrębie nawiasów
duze_litery = [miasto.upper() for miasto in miasta if miasto.startswith('W')]
print(duze_litery)
# w formie pętli byłoby tak:
duze_litery2 = []
for miasto in miasta:
if miasto.startswith('W'):
duze_litery2.append(miasto.upper())
print(duze_litery2)
print()
# Żródłem danych może być dowolna rzecz, dla której zadziała pętla for, dowolne "iterable"
# Bardzo często używa się range
parzyste = list(range(0, 100, 2))
print(parzyste)
parzyste = [x for x in range(0, 100, 2)]
print(parzyste)
parzyste = [2*x for x in range(50)]
print(parzyste)
# wyniki comprehencji można przekazywać od razu do funkcji takich jak sum
# uwaga - to nie jest optymalny zapis - wyjaśnienie dalej w sekcji o generatorach
suma_nieparzystych = sum([2*x+1 for x in range(10)])
print(suma_nieparzystych)
# można wprowadzić kilka zmiennych w kilku forach
tabliczka = [x*y for x in range(1,11) for y in range(1,11)]
print(tabliczka)
print('Suma wszystkich liczb tabliczki mnożenia:', sum(tabliczka))
print()
tabliczka2d = [[x*y for x in range(1,11)] for y in range(1,11)]
print(tabliczka2d)
print()
# Analogicznie: istnieją też set-comprehentions, dict-comprehentions
print(list(range(-5, 6)))
lista = [x*x for x in range(-5, 6)]
print(type(lista))
print(lista)
zbior = {x*x for x in range(-5, 6)}
print(type(zbior))
print(zbior)
generator = (x*x for x in range(-5, 6))
print(type(generator))
print(generator)
# Generator nie wylicza swoich wartości od razu i nie zawiera ich w sobie (jak lista),
# jest tylko przepisem na to jak wyliczać wartości gdy będą potrzebne.
# Dopiero teraz wykonywane są operacje x*x
for y in generator:
print(y, end=' ')
print()
# Dla słowników zapis jest taki:
# {klucz : wartosc for skąd się biorą...}
slownik = {x : x*x for x in range(-5, 6)}
print(type(slownik))
print(slownik)
slownik_miast = {m: len(m) for m in miasta}
print(slownik_miast)
print('\n========\n')
# Tego typu zadanko
suma_nieparzystych = sum([2*x+1 for x in range(10)])
print(suma_nieparzystych)
# może zostać zrealizowane bardziej wydajnie za pomocą generatora
# bo gdy użyjemy generatora, Python nie musi tworzyć w pamięci listy
# do funkcji sum można przekazać wyrażenie bez dodatkowych nawiasów
suma_nieparzystych = sum(2*x+1 for x in range(10))
print(suma_nieparzystych)
suma_tabliczki = sum(x*y for x in range(1,11) for y in range(1,11))
print('Suma liczb z tabliczki mnożenia:', suma_tabliczki)
print()
print('Jakie różne liczby występują w tabliczce mnożenia i ile ich jest')
print(len({x*y for x in range(1,11) for y in range(1,11)}))
print({x*y for x in range(1,11) for y in range(1,11)})
print()
# Inny przykład
# Załóżmy, że chcę stworzyć listę zawierającą kolejne potęgi dwójki od 2**0 do 2**64
# Mogę napisać tradycyjną pętlę i dodawać elementy na koniec listy...
potegi1 = []
for i in range(65):
potegi1.append(2**i)
print(potegi1)
print()
# ... ale w Pythonie można to zrobić sprytniej:
# [wartość for x in ... ]
potegi2 = [2**i for i in range(65)]
print(potegi2)
print()
# Napisy (czyli str) w Pythonie jest sekwencją znaków.
# Można iterować za pomocą pętli for,
# można wycinać fragmenty.
# Stringi są niemutowalne.
napis = 'Ala ma kota'
print(len(napis))
for znak in napis:
print('Kolejny znak:', znak)
print()
print('Typ napisu:', type(napis))
print('Typ znaku:', type(napis[4]))
print(napis[4])
print(napis[4:6])
# Zawartości napisu nie da się zmienić, to nie są tablice takie jak w C. str is immutable
# napis[7] = 'K'
print()
if 'm' in napis:
print('Jest literka m')
else:
print('Nie ma literki m')
# Dla napisów operator in sprawdza czy napis jest fragmentem dużego napisu (a nie tylko czy litera jest elementem)
if 'kot' in napis:
print('kot obecny')
else:
print('nie ma kota')
print('kot jest na pozycji', napis.index('kot'))
print('kot jest na pozycji', napis.find('kot'))
# Gdy nie znajdą: index wyrzuca wyjątek, a find zwraca -1
#ERR print('pies jest na pozycji', napis.index('pies'))
print('pies jest na pozycji', napis.find('pies'))
print()
# Napisy można dodawać - wynikiem jest nowy napis
nowy = napis + ' oraz psa'
print('nowy napis:', nowy)
print('stary napis:', napis)
# Napisy można mnożyć przez liczbę całkowitą - oznacza powtózenie treści
print('Ala ma kota. ' * 10)
print()
# Napisy są "niemutowalne", czyli nie da się zmienić zawartości obiektu typu str.
# Można natmiast do mziennej wpisać inny napis.
a = 'Ala'
b = a
print('a:', a, 'b:', b)
a += ' ma kota'
print('a:', a, 'b:', b)
# a operacje takie jak upper, lower, replace itd nie modyfikują stringa wewnątrz, tylko tworzą i zwracają nowy obiekt
a.replace('ma', 'posiada')
print('a:', a, 'b:', b)
c = a.replace('ma', 'posiada')
print('c:', c)
print()
########
# W Pythonie napisy można podawać w "cudzysłowach" albo w 'apostrofach'
napis = "Ola ma psa"
print(type(napis))
print(napis)
napis = 'Ala ma "kota" a Ola ma \'psa\''
print(napis)
print()
# Napis rozciągający na wiele linii można wpisać za pomocą
# potrójnych znaczków ''' lub """
# Często używa się tego jako komentarzy lub dokumentacji (docstring), ale jest to także normalny napis.
tekst = '''Litwo, 'ojczyzno' moja
ty jesteś jak zdrowie'''
print(type(tekst))
print(tekst)
print()
def funkcja(a,b):
'''
Funkcja dodaje dwie liczby
:param a: pierwsza liczba
:param b: druga liczba
:return: suma liczb a i b
'''
return a + b
wynik = funkcja(5, 10)
print('Dokumentacja tej funkcji:', funkcja.__doc__)
# Jesli chcemy mieć tekst w jednej linii, ale w kodzie programu go podzielić,
# to w ten sposób:
tekst = 'Ala ma kota, ' \
+ 'a Ola ma psa.'
print(tekst)
tekst = ('Ala ma kota, ' +
'a Ola ma psa ')
print(tekst)
# Napisów umieszczonych obok siebie nie trzeba nawet łączyć znakiem +, bo Python sam je połączy
tekst = ('Ala ma kota, '
'a Ola ma psa ' "i chomika")
print(tekst)
print()
# W niektórych sytuacjach w Pythonie używa się specjalnych wersji napisu z magiczną literą na początku.
# f-string - do wklejania i formatowania wartości
ile = 7
print(f'Ala ma {ile} kotów')
print()
# raw-string - aby znaki specjalne nie były przetwarzane.
# Najczęstsze zastosowania: wyrażenia regularne, ścieżki do plików.
# np. w normalnym napisie \t zamienia się w tabulator, \n w znak nowej linii,
# \" i \' w cudzysłów i apostrof, a \\ oznacza pojedynczy \
zwykly = 'Ala\tma\tkota \'Filemona\'\nOla\tma\tpsa\\ \"Burka\", \w wydaje na niego 15\u20ac\n'
print('zwykły', type(zwykly), len(zwykly))
print(zwykly)
print()
print('Natomiast te backslashe się wypiszą: \w\s')
# W surowym stringu (raw-string) te specjalne ciągi są traktowane jak normalna zawartość stringa.
surowy = r'Ala\tma\tkota \'Filemona\'\nOla\tma\tpsa\\ \"Burka\", \w wydaje na niego 15\u20ac'
print('surowy', type(surowy), len(surowy))
print(surowy)
print()
# Zastosowania: ścieżki do plików, wyrażenia regularne
# W Pythonie 2 napisy Unicodowe należało wpisywać w u-stringi
# u'Zażółć gęślą jaźń'
# Operacje str
lista = ['Ala', 'Ola', 'Ela']
napis = ';'.join(lista)
print(napis)
print('Witamy osoby ' + ' oraz '.join(lista) + ".")
napis = 'Ala ma kota, a Ola ma psa. Pomagamy zwierzakom.'
print(napis)
print(napis.replace('ma', 'posiada'))
napis = ' Ala ma kota, a Ola ma psa. '
print(napis.replace(' ', '_'))
print(napis.upper())
print(napis.lower())
print(napis.strip())
print('abcd. efg'.capitalize())
print('abcd efg opr'.title())
print(napis.split())
print(napis.split(','))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment