Moduł collections#
Opis modułu#
Moduł collections zawiera wyspecjalizowane typy danych. Są one
alternatywą dla wbudowanych typów danych takich jak dict
, list
,
set
i tuple
.
Nazwa typu |
Opis |
---|---|
|
Funkcja służąca do tworzenia krotek z nazwanymi polami. |
|
Kontener podobny do listy, z dostępem zarówno od początku, jak i od końca. |
|
Klasa słownika służąca do liczenia obiektów. |
|
Klasa słownika, która pamięta kolejność, w jakiej dodawano elementy. |
|
Klasa słownika, która umożliwia zdefiniowanie brakujących elementów. |
namedtuple
#
Nazwane krotki są stosowane wszędzie tam, gdzie potrzebujemy manipulować niewielkimi rekordami, a jednocześnie nie chcemy tworzyć osobnej klasy.
Przykładem jest klasa Point
:
from collections import namedtuple
import math
Point = namedtuple('Point', 'x y z')
def distance(a, b):
return math.sqrt(
(a.x - b.x)**2 +
(a.y - b.y)**2 +
(a.z - b.z)**2
)
a = Point(x=1, y=2, z=3)
b = Point(-1, -2, 42)
print(distance(a, b))
39.25557285278104
Nazwane krotki przydają się także wtedy, gdy chcemy zwrócić więcej niż jeden obiekt. Funkcja lub metoda może zwrócić kilka obiektów w krotce, ale trzeba wówczas pamiętać, w jakiej kolejności są one zwracane. Alternatywą jest zwrócenie nazwanej krotki:
from collections import namedtuple
Result = namedtuple('Result', 'quotient remainder')
def div_mod(a, b):
return Result(quotient=a//b, remainder=a%b)
r = div_mod(9, 2)
r
Result(quotient=4, remainder=1)
r.quotient
4
r.remainder
1
Deque#
W niektórych algorytmach stosowane są kolejki FIFO (first in, first out). Kolejka jest listą, której nowe elementy są dodawane na jej początku. Z kolei inny wątek może pobierać elementy (odczytywać je i usuwać z listy) z jej końca.
Stosowanie listy może spowolnić program, ponieważ dodanie lub usunięcie
elementu z początku listy jest operacją kosztowną (złożoność O(n)).
Dlatego w takich sytuacjach stosuje się wyspecjalizowany kontener
deque
. Jego zaletą jest możliwość szybkiego (w czasie stałym
O(1)) dodawania i usuwania elementów zarówno z początku, jak i końca.
[deque]{.title-ref} udostępnia ten sam interfejs, co listy. Dostępne są również metody appendleft
, extendleft
oraz popleft
, które
działają tak samo jak odpowiednio append
, extend
i pop
, ale
działają na początku kolekcji zamiast na jej końcu.
Na przykład, appendleft(x)
wstawia obiekt x na początek kolejki. Jest
to równoważne wywołaniu insert(0, x))
.
import collections
dq = collections.deque('abcdefg')
dq
deque(['a', 'b', 'c', 'd', 'e', 'f', 'g'])
len(dq)
7
dq[0]
'a'
dq[-1]
'g'
dq.remove('c')
dq
deque(['a', 'b', 'd', 'e', 'f', 'g'])
while True:
try:
print(dq.pop())
except IndexError:
break
g
f
e
d
b
a
dq = collections.deque('abcdefg')
while True:
try:
print(dq.popleft())
except IndexError:
break
a
b
c
d
e
f
g
dq = collections.deque(range(10))
dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
dq = collections.deque(range(10))
dq.rotate(2)
dq
deque([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
dq = collections.deque(range(10))
dq.rotate(-2)
dq
deque([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])
Counter#
Counter
jest klasą ułatwiającą zliczanie elementów.
Jest to słownik, którego kluczami są elementy, a wartościami częstość ich występowania.
Przy tworzeniu instancji należy przekazać kolekcję elementów, które mają zostać zliczone.
Zliczanie znaków występujących w danym napisie można wykonać przy pomocy zwykłego słownika:
counter = {}
for char in "Hello there, People!":
if char not in counter:
counter[char] = 1
else:
counter[char] += 1
print(counter)
{'H': 1, 'e': 5, 'l': 3, 'o': 2, ' ': 2, 't': 1, 'h': 1, 'r': 1, ',': 1, 'P': 1, 'p': 1, '!': 1}
Lub szybciej, przy pomocy klasy Counter
:
from collections import Counter
counter = Counter("Hello there, People!")
print(counter)
Counter({'e': 5, 'l': 3, 'o': 2, ' ': 2, 'H': 1, 't': 1, 'h': 1, 'r': 1, ',': 1, 'P': 1, 'p': 1, '!': 1})
most_common
jest metodą umożliwiającą sprawdzenie, które elementy występują najczęściej.
Jeśli chcemy znaleźć najczęściej występujące słowa w pliku holmes.txt
, możemy wykonać:
import re
words = re.findall('\w+', open('holmes.txt').read().lower())
print(Counter(words).most_common(10))
[('the', 5810), ('and', 3088), ('i', 3038), ('to', 2823), ('of', 2778), ('a', 2700), ('in', 1823), ('that', 1767), ('it', 1749), ('you', 1572)]
OrderedDict#
Słowniki przechowują pary (klucz, wartość), ale nie pamiętają kolejności, w jakiej poszczególne pary zostały dodane.
OrderedDict
jest słownikiem, który zapamiętuje tę kolejność.
Podczas iterowania po nim, zwraca on klucze w kolejności, w jakiej zostały dodane:
from collections import OrderedDict
d = OrderedDict()
d['c'] = 1
d['b'] = 2
d['a'] = 1
for k in d:
print(k)
c
b
a
Dodatkowo, ten obiekt udostępnia metodę popitem
:
d.popitem() # zwraca ostanio dodaną parę (klucz, wartość) (kolejka LIFO)
('a', 1)
d.popitem(last=False) # zwraca pierwszą dodaną parę (kolejka FIFO)
('c', 1)
defaultdict
#
defaultdict
jest takim słownikiem, w którym użycie klucza nie będącego w tym słowniku powoduje użycie domyślnej wartości zamiast zgłoszenia wyjątku KeyError
.
Ten obiekt pozwala uprościć kod wykorzystujący ideę multisłownika, to znaczy słownika, w którym jeden klucz może mieć więcej niż jedną wartość. Taki multisłownik może zostać zaimplementowany jako zwykły słownik, którego wartościami jest lista.
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
res = {}
for color, item in s:
if color in res:
res[color].append(item)
else:
res[color] = [item, ]
res.items()
dict_items([('yellow', [1, 3]), ('blue', [2, 4]), ('red', [1])])
Rozwiązanie z użyciem defaultdict
jest znacznie prostsze.
Przy tworzeniu słownika przekazujemy funkcję, która generuje wartość domyślną.
W tym przypadku wartością domyślną jest pusta lista, którą generuje wbudowana funkcja list
.
from collections import defaultdict
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
print(d)
defaultdict(<class 'list'>, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})