Języki skryptowe Python

Wykład 4

Typy mutowalne (mutable) i niemutowalne (immutable)


  • typy liczbowe ("niemutowalne"): int, float, complex
  • typy sekwencyjne ("mutowalne"): list
  • typy sekwencyjne ("niemutowalne"): tuple, range, str

id zmiennych


x = 2  # tworzymy niemutowalną zmienną
id(x)  # i sprawdzamy jej id
140685772794432
x = 3  # przypisanie nowej wartości 
id(x)  # tworzy nowy obiekt (z nowym id)
140685772794464
x = [2]  # tworzymy mutowalną zmienną 
id(x)    # i sprawdzamy jej id
140685552461384
x[0] = 3  # wartość wskazywana przez zmienną jest modyfikowana
id(x)     # ale jej id pozostaje stałe (uwaga: id(x[0]) będzie inne!)
140685552461384

Konsekwencje


x = 2  # uwtórz obiekt typu int
y = x  # y wskazuje na to samo co x
y = 5  # zmieniam y - nowe miejsce w pamięci

print(x)  # x dalej wskazuje na 2
2
x = [1, 2, 3]  # utwórz obiekt typu list
y = x          # y wskazuje na to samo co x

print(id(x), id(y))
140684962055816 140684962055816
y[0] = 4  # modyfikuję y

print(x)  # a zmienia się x...
[4, 2, 3]

Funkcja copy


x = [1, 2, 3]  # utwórz obiekt typu list
y = x.copy()   # stwórz kopię tego obiektu

print(id(x), id(y))  # teraz są to dwa różne obiekty
140685552556616 140685552460232

"Odpakowywanie" (unzip)


lista = [1, 2]

a, b = lista  # a = lista[0], b = lista[1]

print(a, b)
1 2

przykład: zamiana zmiennych

a, b = b, a  # a, b = tuple(b, a)

print(a, b)
2 1

dla porównania przykład zamiany zmiennych w C++

a = a + b - (b = a);

splat, czyli tzw. asterisk


x = [1, 2, 3]

# print(arg1, arg2, arg3, ..., +opcje)

print(x)                 # drukuje listę (1 argument)
print(*x)                # drukuje "rozpakowaną" listę (3 argumenty)
print(x[0], x[1], x[2])  # równoważny zapis
[1, 2, 3]
1 2 3
1 2 3

unzip i splat


lista = [1, 2, 3, 4, 5]

# a, b = lista  # ValueError: too many values to unpack (expected 2)

a, *b = lista  # a = lista[0], b = reszta

print(a, b)
1 [2, 3, 4, 5]
lista = [1, 2, 3, 4, 5]

a, *b, c = lista  # a, b[0], b[1], b[2], c

print(a, b, c)
1 [2, 3, 4] 5

"Pakowanie" (zip)


x = [1, 2, 3]
y = ['a', 'b', 'c']

zipped = zip(x, y)  # pary (x[i], y[i])

print(*zipped)
(1, 'a') (2, 'b') (3, 'c')
x = [1, 2, 3]
y = ['a', 'b', 'c', 'd']  # długość nie ma znaczenia

zipped = zip(x, y)  # pary (x[i], y[i])

print(*zipped)
(1, 'a') (2, 'b') (3, 'c')

zip / unzip - test


x = [1, 2, 3]
y = [4, 5, 6]

# pakujemy rozpakowaną paczkę
x_copy, y_copy = zip(*zip(x, y))

# i otrzymujemy znowu te same listy
x == list(x_copy) and y == list(y_copy)
True
# jak to działa krok po kroku
print(list(zip(x,y)))         # lista par i-tych elementów
print(*zip(x,y))              # rozpakowana lista
print(list(zip(*zip(x, y))))  # parujemy i-te elementy
[(1, 4), (2, 5), (3, 6)]
(1, 4) (2, 5) (3, 6)
[(1, 2, 3), (4, 5, 6)]

Pętla for z indeksami


lista = ['a', 'b', 'c']
# pętla po indeksach od 0 do N-1
for i in range(len(lista)):
    print(str(i+1) + '.', lista[i])
# pętla po spakowanych indeksach i elementach listy
for i, element in zip(range(len(lista)), lista):
    print(str(i+1) + '.', element)
# pętla po spakowanych indeksach i elementach listy z użyciem enumerate
for i, element in enumerate(lista):
    print(str(i+1) + '.', element)
1. a
2. b
3. c

Formatowanie tekstu


help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

sep i end


a, b, c = 1, 2, 3

print(a, b, c)           # domyślnym separatorem jest spacja
1 2 3
print(a, b, c, sep='_')  # ale można go zmiennić
1_2_3
# domyślnie print kończy sekwencję nową linią, ale można to zmienić
print(a, b, c, sep="...", end=" koniec")
1...2...3 koniec

format - podstawy


x = 2

print("x jest równe " + str(x))  # skuteczne, ale mało wygodne
x jest równe 2
x = 2

# klasa string ma metodę format
print("x jest równe {}".format(x))
x jest równe 2
# która w miejsce {} wstawia kolejne argumenty
print("x jest równe {}, a x**2 = {}".format(x, x**2))
x jest równe 2, a x**2 = 4

format - kolejność


x = 2
y = 2.5
z = "trzy"

# domyślnie pod {} wstawiane są kolejne argument format
print("x, y, z = {}, {}, {}".format(x, y, z))
x, y, z = 2, 2.5, trzy
# ale kolejność można zmienić
print("x, y, z = {2}, {0}, {1}".format(x, y, z))
x, y, z = trzy, 2, 2.5

format - deklaracja typu


x = 1234567890
y = 1234567890.1234567890
z = "Python"

# z reguły nie ma potrzeby jawnej deklaracji typu
print("x, y, z = {}, {}, {}".format(x, y, z))
x, y, z = 1234567890, 1234567890.1234567, Python
# ale można to zrobić: d - int; f - float; s - str
print("x, y, z = {:d}, {:f}, {:s}".format(x, y, z))
x, y, z = 1234567890, 1234567890.123457, Python
# e - wygodny format dla dużych liczb: XeY = X * 10^Y
print("x, y, z = {:f}, {:e}, {}".format(x, y, z))
x, y, z = 1234567890.000000, 1.234568e+09, Python

format - dokładność


from math import pi

print("pi = {}".format(pi))
pi = 3.141592653589793
# ograniczamy się do dwóch liczb po przecinku
print("pi = {:.2f}".format(pi))
pi = 3.14
# ograniczamy się do 50 liczb po przecinku
# zwróć uwagę na zera na końcu
print("pi = {:.50f}".format(pi))
pi = 3.14159265358979311599796346854418516159057617187500

format - keyword arguments


# żeby się nie pogubić, warto "tagować" kolejne argumenty
print("{student} otrzymał {ocena}".format(student="Jan Nowak", ocena=5))
Jan Nowak otrzymał 5
# wtedy kolejność nie ma znaczenia
print("{student} otrzymał {ocena}".format(ocena=5, student="Jan Nowak"))
Jan Nowak otrzymał 5

# lista studentów
studenci = ["Kasia", "Basia", "Marek", "Józek"]
# każdy element odpowiada liście ocen danego studenta
dziennik = [[3, 4, 5], [], [5, 3], [3, 2, 2, 2, 2]]

# [znak]^N -> centruj w szerokości N wypełniając [znakiem]
print("{:-^42}".format("OCENY"), end="\n\n")

# enumerate zwraca dwa obiekty: index i parę: (student, oceny)
for i, (student, oceny) in enumerate(zip(studenci, dziennik)):
    # policz średnią; chyba że brak ocen
    if len(oceny):
        srednia = sum(oceny) / len(oceny)
    else:
        srednia = 0

    # {oceny:<15} działa jak ljust; {oceny:>15} działa jak rjust
    print("{index}. {imie}: {oceny:<15} => srednia = {srednia:.1f}"
          .format(index=i+1,
                  imie=student,
                  oceny=str(oceny),
                  srednia=srednia))
------------------OCENY-------------------

1. Kasia: [3, 4, 5]       => srednia = 4.0
2. Basia: []              => srednia = 0.0
3. Marek: [5, 3]          => srednia = 4.0
4. Józek: [3, 2, 2, 2, 2] => srednia = 2.2

format - nowa składnia


  • Od wersji 3.6 możliwe jest korzystanie z nowej składni
x = 1
y = 2

# równoważne: `print("x = {}, y = {}".format(x, y))`
print(f"x = {x}, y = {y}")

Słownik (dict)


  • mapuje obiekty hashowalne (hashable) w dowolne obiekty, czyli: klucz: wartość
    • klucz - stały hash
    • wartość - dowolny obiekt
  • np. krotka może być kluczem, ale lista już nie
  • słowniki tworzymy umieszczając oddzielone przecinkiem pary key: value w nawiasach klamrowych, np.
# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

print(slownik)
{'Kasia': 7236, 'Basia': 5286, 'Darek': 7738, 'Marek': 9807}

Słownik przez konstruktor


# {key1: value1, key2: value2...}
a = {"jeden": 1, "dwa": 2, "trzy": 3}

# dict(key1=value1, key2=value2...)
b = dict(jeden=1, dwa=2, trzy=3) 

# dict([(key1, value1), (key2, value2)...])
c = dict(zip(["jeden", "dwa", "trzy"], [1, 2, 3]))

# dict(słownik)
d = dict(a)

a == b == c == d
True

Słownik - dostęp do wartości


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

slownik["Kasia"]  # dostęp -> dict[key]
7236
slownik[0]  # słownik nie jest uporządkowany
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-4-8f2cb9210d58> in <module>()
----> 1 slownik[0] # słownik nie jest uporządkowany


KeyError: 0

Słowniki - KeyError


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

print(slownik["Kasia"])  # Kasia jest w słowniku -> OK
print(slownik["Ania"])   # Ani nie ma -> KeyError
7236
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-5-7f55cd27f397> in <module>()
        3 
        4 print(slownik["Kasia"]) # Kasia jest w słowniku -> OK
----> 5 print(slownik["Ania"])  # Ani nie ma -> KeyError


KeyError: 'Ania'

Słowniki - get


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

print(slownik.get("Kasia"))       # Kasia jest w słowniku -> OK
print(slownik.get("Ania"))        # Ani nie ma -> default = None
print(slownik.get("Ania", 1234))  # Ani nie ma -> default = 1234
7236
None
1234
# czemu domyślnie None?
if slownik.get("Ania"):
    print(slownik["Ania"])
else:
    print("Brak studenta.")

# lub prościej: `print(slownik.get("Ania") or "Brak studenta.")`
Brak studenta.

Słownik - klucze i wartości


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}
slownik.keys()    # lista kluczy
dict_keys(['Kasia', 'Basia', 'Darek', 'Marek'])
slownik.values()  # lista wartości
dict_values([7236, 5286, 7738, 9807])
slownik.items()   # lista par
dict_items([('Kasia', 7236), ('Basia', 5286), ('Darek', 7738), ('Marek', 9807)])

Słownik - Python 2 vs 3


  • w Pythonie 2: dict.keys(), dict.values() i dict.items() zwracają listy (czyli dopuszczalne jest np. dict.keys()[0])
  • w Pythonie 3:
import collections # for Iterable

# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

# slownik.keys()[0] # TypeError: 'dict_keys' object does not support indexing

isinstance(slownik.keys(), list)
False
isinstance(slownik, collections.Iterable)  # pętla po keys() -> OK
True

Słownik - modyfikacja kluczy


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

slownik["Kasia"] = 1234  # dict[key] = value

print(slownik)
{'Kasia': 1234, 'Basia': 5286, 'Darek': 7738, 'Marek': 9807}
slownik["Kasia"] += 1    # analogicznie do listy: list[index]

print(slownik)
{'Kasia': 1235, 'Basia': 5286, 'Darek': 7738, 'Marek': 9807}

Słownik - usuwanie kluczy


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

del slownik["Marek"]  # del dict[key] -> usuń klucz

print(slownik)
{'Kasia': 7236, 'Basia': 5286, 'Darek': 7738}
slownik.clear()  # wyczyść słownik
 
print(slownik)
{}

Słownik - dodawanie kluczy


slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

slownik["Ania"] = 3384  # jeśli jest to zmień, jeśli nie ma to dodaj
print(slownik)
{'Kasia': 7236, 'Ania': 3384, 'Basia': 5286, 'Darek': 7738, 'Marek': 9807}
nowi_studenci = {"Romek": 3343, "Basia": 8573}

slownik.update(nowi_studenci)  # "dodaj/scal" słowniki
print(slownik)                 # zwróć uwagę na Basię
{'Basia': 8573, 'Romek': 3343, 'Darek': 7738, 'Kasia': 7236, 'Ania': 3384, 'Marek': 9807}
slownik.update(Józek=2276)
print(slownik)
{'Basia': 8573, 'Romek': 3343, 'Darek': 7738, 'Józek': 2276, 'Kasia': 7236, 'Ania': 3384, 'Marek': 9807}

Pętla po słowniku


# student: numer indeksu
slownik = {"Kasia": 7236, "Basia": 5286, "Marek": 9807, "Darek": 7738}

for student in slownik.keys():          # pętla po kluczach
    print(student, end=' ')
Kasia Basia Darek Marek 
for index in slownik.values():          # pętla po wartościach
    print(index, end=' ')
7236 5286 7738 9807 
for student, index in slownik.items():  # pętla po (klucz, wartość)
    print(student, index)
Kasia 7236
Basia 5286
Darek 7738
Marek 9807

Przykład - tworzenie słownika


studenci = {}  # stwórz pusty słownik

while True:
    # pobierz imię studenta
    student = input("Imię: ")
    # przerwij jeśli puste
    if not student: break  # niezalecane, ale możliwe w jednej linii
    # pobierz numer indeksu
    index = input("Nr indeksu: ")
    # aktualizuj słownik
    studenci.update({student: index})
    
print(studenci)
Imię: Kasia
Nr indeksu: 1234
Imię: Jasiu
Nr indeksu: 0987
Imię: 
{'Kasia': '1234', 'Jasiu': '0987'}

Słownik w słowniku


Kasia = {"Wiek": 20, "Wzrost": 190, "Waga": 70}
Marek = {"Wiek": 22, "Wzrost": 180, "Waga": 80}

# klucz musi być hashowalny
# wartością może być dowolny obieky, np. słownik
studenci = {"Kasia": Kasia, "Marek": Marek}

# wydrukuj w pętli wszystkich studentów i ich atrybuty
for imie, wlasnosci in studenci.items():
    print("{student} ma {wiek} lat, "
          "{wzrost} cm wzrostu "
          "i waży {waga} kg.".format(
            student=imie,
            wiek=studenci[imie]["Wiek"],
            wzrost=studenci[imie]["Wzrost"],
            waga=studenci[imie]["Waga"])
          )
Kasia ma 20 lat, 190 cm wzrostu i waży 70 kg.
Marek ma 22 lat, 180 cm wzrostu i waży 80 kg.

dict comprehension


# przypomnienie: lista składana
lista = [x**2 for x in range(10)]
# krotka składana (bez `*` i `,` - generator, będzie na kolejnych wykładach)
krotka = *(x**2 for x in range(10)),

print(lista, krotka, sep='\n')
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
# analogicznie można tworzyć słownik
slownik = {x: x**2 for x in range(10)}

print(slownik)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}