Spis treści

Advent of code

Kilka dni temu rozpoczął się Advent of code. W tym roku, podobnie jak w zeszłym, wzięłam na warsztat Pythona. Odkrywam ten język właściwie na nowo - po wielu latach (i wielu nowych wydaniach) zaszło w nim wiele zmian. Oto kilka perełek, które odkryłam w trakcie rozwiązywania tegorocznych zadań:

Sortowanie topologiczne - moduł graphlib

Napisałam o sortowaniu topologicznym odrębny artykuł. Od Pythona 3.9 będzie można używać modułu graphlib.

str.format

Użycie str.format (wraz z mini-językiem) zamiast formatowania przy użyciu % (które ma dość ograniczone możliwości: %-formatownie pozwala jedynie na użycie typów int, str i double):

1
2
3
4
5
6
7
8

   # str.format zamiast formatowania przy użyciu % 
   number = 230
   data = 0X24f
   print("The number is {0:+} (hex: {0:#}) \
    and data is (from hex): {1:_^#10}".format(number, data))

   # The number is +230 (hex: 230) and data is (from hex): ___591____

Ten mini-język został wprowadzony przy formatowanych literałach napisowych(PEP-498) i daje dużo swobody w wypisywaniu wartości:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

  class Task:
    def __init__(self, prio, desc):
      self.prio = prio
      self.desc = desc

  tasks = [
    (1, "wyrzuć śmieci"), 
    (10, "nastaw pranie"), 
    (2, "poćwicz jogę"), 
    (2, "poczytaj")]
  for i,t in enumerate(sorted(tasks, key=lambda p: p[0])):
    print("{i})  Prio {v[0]: >5}: zadanie: {v[1]}".format(v=t, i=i))

  
  #0)  Prio     1: zadanie: wyrzuć śmieci
  #1)  Prio     2: zadanie: poćwicz jogę
  #2)  Prio     2: zadanie: poczytaj
  #3)  Prio    10: zadanie: nastaw pranie
  

  d = deque(map(Task, tasks))
  while d:
    el = d.pop()
    print("popped task {t.desc} with priority {t.prio}")

set.difference

Użycie set.difference (and other set-specific operations):

1
2
3
4
5
  s1 = set("one two two three six one".split(" "))
  s2 = set("one six".split(" "))
  print(s1.difference(s2))

  # result: {"two", "three"}

Aktualizacja słownika: dict - union

Do klasy [dict] dodany został operator [union]. Istnieje kilka sposobów "łączenia" słowników, żaden z nich jednak nie wydaje się wystarczająco elegancki. W specyfikacji PEP-0584 wymmienione zostały między innymi:

Zmiana słownika w miejscu

Modyfikuje słownik d1 (tracimy oryginalne dane)

1
d1.update(d2)

Rozpakowanie istniejących słowników

Można też utworzyć nowy słownik przy pomocy rozpakowania istniejąch słowników d1 i d2:

1
{**d1,**d2}

To rozwiązanie nie zachowuje się dobrze dla podtypów [dict] (zawsze typem rezultatu jest [dict], niezależnie od typów składowych).

collections.ChainMap

Wrapper na dwa słowniki. Problematyczny w przypadku konfliktu kluczy: stosuje zasadę "najwcześniejsza wartość wygrywa". Dodatkowo modyfikacja zmienia słownik pod spodem.

1
2
3
4
5
6
    d1 = {"A": 100}
    d2 = {"B": 200}
    merged = ChainMap(d1, d2)
    merged["B"] = 7
    d2
    # {B: 7}

Operator union (znak | - “pipe”)- nowość w 3.9

Dodany w Pythonie 3.9 operator union, który rozwiązuje konflikty kluczy we właściwy (to znaczy: najbardzej intuicyjny) sposób: ostatnia wartość wygrywa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

    d1 = {3: "ala", 5: "ma", 10: "kota"}
    d2 = {3: "kasia", 10: "psa"}
    d1 | d2
    # {3: "kasia", 5: "ma", 10: "psa"}
    d1
    # d1 nie zmienił się 
    # {3: "ala", 5: "ma", 10: "kota"}
    d2
    # d2 nie zmienił się
    # {3: "kasia", 10: "psa"}