Rzadko wykorzystywane typy danych

Wstęp

Przy codziennej pracy z pythonem często korzystasz z takich typów danych jak słownik, lista, krotka czy zbiór. Ale warto pamiętać, że istnieją również inne typy danych, które często mogą Ci się przydać, a o których niekoniecznie słyszałeś. Przedstawię kilka z nich, które moim zdaniem są warte uwagi.

Counter

Typ ten przydaje się wtedy gdy chcesz np. sprawdzić ile razy występuje dany obiekt w zbiorze i dodatkowo chcesz to trzymać jako słownik. Przykład:

>>> from random import randint
>>> from collections import Counter
>>> l = [randint(0, 333) for x in xrange(100)]
>>> c = Counter(l)
>>> c.most_common(5)
[(286, 3), (314, 3), (133, 2), (139, 2), (142, 2)]
>>> c[286]
3
view raw counter.py hosted with ❤ by GitHub

deque

Obiekt deque idealnie nadaje się do stosu lub kolejki. Dodawanie oraz pobieranie elementu jest zbliżone do O(1); nie ważne czy pobierasz z końca czy z początku. Jeżeli jednak chcesz dostać się do elementów po indeksach lepiej jest skorzystać z listy, która jest do tego przystosowana, więc siłą rzeczy jest szybsza. Poniżej, krótkie porównanie:

>>> from collections import deque
>>> l = deque([])
>>> for x in xrange(15):
>>> l.appendleft(x)
>>> l.append(x)
>>> print l
deque([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
>>> for x in xrange(30):
>>> l.popleft()
>>> print l
deque([])
>>> from timeit import timeit
>>> def stack_deque():
>>> l = deque([])
>>> for x in xrange(10000):
>>> l.appendleft(x)
>>> timeit(stack_deque, number=1000)
0.6624810695648193
>>> def stack_list():
>>> l = []
>>> for x in xrange(10000):
>>> l.insert(0, x)
>>> timeit(stack_list, number=1000)
23.359800100326538
>>> def stack_deque_two():
>>> l = deque([x for x in xrange(10000)])
>>> for x in xrange(10000):
>>> l.popleft()
>>> timeit(stack_deque_two, number=1000)
0.9383018016815186
>>> def stack_list_two():
>>> l = [x for x in xrange(10000)]
>>> for x in xrange(10000):
>>> l.pop(0)
>>> timeit(stack_list_two, number=1000)
15.480417013168335
view raw deque.py hosted with ❤ by GitHub

defaultdict

defaultdict przydaje się tam, gdzie chcesz stworzyć słownik w którym przy dostępie ma już mieć zdefiniowaną domyślną wartość. Zamiast martwić się sprawdzaniem czy czasem dany klucz ma zdefiniowaną wartość, sam ustawiasz jaki to ma być typ.

>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> for x in ["hell", "camp", "four", "walls", "only", "hell", "pity", "party"]:
>>> d[x] += 1
>>> print d["hell"]
2
>>> print d["pity"]
1
>>> print d
defaultdict(<type 'int'>, {'pity': 1, 'camp': 1, 'only': 1, 'four': 1, 'walls': 1, 'party': 1, 'hell': 2})
>>> d = defaultdict(list)
>>> for x in ["hell", "four", "walls", "hell", "pity", "party"]:
>>> d[x].append(True)
>>> print d["hell"]
[True, True]
>>> print d["walls"]
[True]
>>> print d
defaultdict(<type 'list'>, {'four': [True], 'hell': [True, True], 'walls': [True], 'pity': [True], 'party': [True]})
view raw defaultdict.py hosted with ❤ by GitHub

namedtuple

Ten typ danych jest często wykorzystywany przy pobieraniu danych z bazy lub np. z pliku csv. Ja napisałem prosty przykład jak szybko przemienić otrzymanego JSON'a w namedtuple i mieć dostęp do wartości nie po kluczu, a po atrybucie.
>>> from __future__ import division
>>> from collections import namedtuple
>>> json_data = {
>>> "id": 63082999,
>>> "title": "Le retour",
>>> "upload_date": "2013-04-01 08:46:22",
>>> "user_id": 13095550,
>>> "user_name": "Natalia Chernysheva",
>>> "likes": 930,
>>> "plays": 50586,
>>> "comments": 45,
>>> "duration": 87,
>>> "width": 1280,
>>> "height": 720,
>>> "tags": ""
>>> }
>>> VimeoStats = namedtuple(
>>> "VimeoStats",
>>> "id, title, upload_date, user_id, user_name, likes, plays, comments, duration, width, height, tags"
>>> )
>>> v = VimeoStats(**json_data)
>>> print v.user_name
Natalia Chernysheva
>>> print v.plays / v.likes
54.3935483871
>>> print v.width / v.height
1.77777777778
view raw namedtuple.py hosted with ❤ by GitHub

heapq

heapq przyda się wtedy, gdy chcesz stworzyć kopiec (kolejka priorytetowa). Przykład użycia:

>>> import heapq
>>> from random import randint
>>> my_heap = []
>>> for _ in xrange(20):
>>> heapq.heappush(my_heap, randint(0, 3000))
>>> print my_heap
[134, 541, 156, 895, 706, 1700, 427, 2049, 1108, 1092, 1895, 2807, 2469, 811, 717, 2962, 2992, 2356, 1935, 2834]
>>> print heapq.heappop(my_heap)
134
>>> print heapq.heappop(my_heap)
156
>>> print heapq.heappop(my_heap)
427
view raw heapq.py hosted with ❤ by GitHub

bisect

Wykorzystanie modułu bisect nadaje się wtedy, gdy masz wystarczająco dużą listę, w której sortowanie jej za każdym razem nie wchodzi w grę, ale w jakiś sposób chcesz otrzymać wartość, która znajdowała by się pod indeksem 42, gdyby lista była posortowana. Prosty przykład wraz z porównaniem:

>>> import bisect
>>> from timeit import timeit
>>> from random import randint
>>> def bisect_sort():
>>> l = [randint(0, 300000) for x in xrange(1000000)]
>>> bisect.bisect_left(l, 42)
>>> print timeit(bisect_sort, number=100)
117.30470705
>>> def list_sort():
>>> l = [randint(0, 300000) for x in xrange(1000000)]
>>> l.sort()
>>> l[42]
>>> print timeit(list_sort, number=100)
222.244460821
view raw bisect.py hosted with ❤ by GitHub

Comments

Popular posts from this blog

GitLab - extends keyword

Managing Secrets in GitLab / Git

GitLab - trigger keyword