Everyone knows Python's basic datatypes and their most common containers (list, tuple, dict and set).
However, few people know that they should use a deque to implement a queue, that using defaultdict their code would be cleaner and that they could be a bit more efficient using namedtuples instead of creating new classes.
This talk will review the data structures of Python's "collections" module of the standard library (namedtuple, deque, Counter, defaultdict and OrderedDict) and we will also compare them with the built-in basic datatypes.
2. Today  we  are  going  to  talk  about  the Â
(unknown)  collections  module
And  also  about  built-Âââin  containers
Welcome!
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
3. âThis  module  implements Â
specialized  container  datatypes Â
providing  alternatives  to  Pythonâs Â
general  purpose  built-Âââin  containers, Â
dict,  list,  set,  and  tupleâ
The  collections  module
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
4. Letâs  start  with  Pythonâs  most  used Â
container
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
5. Yes,  thatâs  dict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
6. >>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d['b']
2
>>> d['d'] = 4
>>> d
{'d': 4, 'b': 2, 'c': 3, 'a': 1}
>>> d['e']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'e'
>>> print(d.get('e'))
None
>>> d.get('e', 5)
5
dict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
7. OPERATION
AVERAGE
AMORTIZED Â WORST
Get  item   d['b']
O(1)
O(n)
Set  item    d['d'] = 4
O(1)*
O(n)
Delete  item   del d['b']
O(1)
O(n)
Copy   new_d = dict(d)
O(n)
O(N)
Iteration   for k in d:
O(n)
O(N)
>âŻ
Internally  implemented  with  an  optimised  hash  map
>âŻ
*:  Amortized  cost.  Individual  ops  may  be  really  slow
>âŻ
N:  Maximum  size  the  container  ever  achieved
dict  performance
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
8. And  what  about  set?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
9. >>> vowels = {'a', 'e', 'i', 'o', 'u'}
>>> letters = set(['a', 'b', 'c', 'd', 'e'])
>>> vowels â letters
{'i', 'o', 'uâ}
>>> vowels & letters
{'a', 'eâ}
>>> vowels | letters
{'u', 'i', 'o', 'c', 'b', 'a', 'e', 'dâ}
>>> vowels ^ letters
{'u', 'i', 'o', 'c', 'b', 'd'}
>>> 'b' in letters
True
>>> letters.add('a')
>>> letters
{'c', 'b', 'a', 'e', 'd'}
>>> letters.update(['d', 'e', 'f', 'g'])
>>> letters
{'c', 'b', 'a', 'g', 'f', 'e', 'd'}
set
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
10. OPERATION
AVERAGE
AMORTIZED Â WORST
Check  item     'b' in s1
O(1)
O(n)
Union                  s1 | s2
O(len(s1) Â + Â len(s2))
Intersection        s1 & s2
O(min(len(s1), Â len(s2)))
DiďŹerence           s1 â s2
O(len(s1))
Symmetric  diďŹ Â Â s1 ^ s2 Â
O(len(s1))
O(len(s1) Â * Â len(s2))
>âŻ
>âŻ
O(len(s1) Â * Â len(s2))
Implementation  very  similar  to  dicts  (hash  map)
Also  has  in-Âââplace  modiďŹcation  methods  (its  average Â
cost  depends  on  s2)
set  performance
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
11. A  bit  boring,  isnât  it?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
12. Let'ʚs  do  something  more  appealing
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
13. txt = """El desconocido mĂłdulo Collections
Todo el mundo conoce los tipos bĂĄsicos de Python y sus
contenedores mĂĄs comunes (list, tuple, dict y set). En cambio,
poca gente sabe que para implementar una cola deberĂa utilizar
un deque, que con un defaultdict su cĂłdigo quedarĂa mĂĄs limpio y
serĂa un poco mĂĄs eficiente o que podrĂa utilizar namedtuples en
lugar de crear nuevas clases. En esta charla repasaremos las
estructuras del mĂłdulo collections de la librerĂa estĂĄndar:
namedtuple, deque, Counter, OrderedDict y defaultdict. Veremos
su funcionalidad, particularidades y casos prĂĄcticos de uso.
Pablo Enfedaque Vidal
Trabajo como R&D SW Engineer en TelefĂłnica PDI en Barcelona, y
desde hace mĂĄs de 5 aĂąos casi exclusivamente con Python, un
lenguaje que me encanta"""
During  the  talk  we  will  use  this  str
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
14. >>> initials = {}
def classify_words(text):
for word in text.split():
word = word.lower()
if word[0] in initials:
initials[word[0]].append(word)
else:
initials[word[0]] = [word, ]
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
Letâs  classify  words
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
15. >>> initials = {}
def classify_words(text):
for word in text.split():
word = word.lower()
if word[0] in initials:
initials[word[0]].append(word)
else:
initials[word[0]] = [word, ]
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
Does  it  look  pythonic?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
16. >>> initials = {}
def classify_words(text):
for word in text.split():
word = word.lower()
initials.setdefault(word[0], []).append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
What  about  now?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
17. from collections import defaultdict
>>> initials = defaultdict(list)
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
collections.defaultdict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
18. from collections import defaultdict
>>> initials = defaultdict(list)
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> initials.default_factory
<class 'list'>
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
collections.defaultdict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
19. >âŻ
defaultdict  is  a  subclass  of  the  built-Âââin  dict  class
>âŻ
The  ďŹrst  argument  provides  the  initial  value  for  the Â
default_factory  a2ribute  (it  defaults  to  None)
>âŻ
All  remaining  arguments  are  treated  the  same
>âŻ
It  also  overrides  the  __missing__  method  to  call  the Â
default_factory  when  an  key  is  not  found
>âŻ
default_factory  may  raise  an  exception  (e.g.  KeyError)
>âŻ
Since  Python  2.5
collections.defaultdict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
20. Letâs  continue  classifying  words
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
21. class WordsByInitial():
"Holds initial letter and a set and a list of words"
def __init__(self, letter):
self.letter = letter
self.words = []
self.unique_words = set()
def append(self, word):
self.words.append(word)
self.unique_words.add(word)
def __str__(self):
return "<{}: {} {}>".format(self.letter,
self.unique_words,
self.words)
>>> a_words = WordsByInitial('a')
>>> a_words.append('ahora')
>>> a_words.append('adios')
>>> a_words.append('ahora')
>>> print(a_words)
<a: {'adios', 'ahora'} ['ahora', 'adios', 'ahora']>
Now  we  have  this  custom  class
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
22. What  if  we  want  to  use  our  class  with Â
defaultdict?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
23. class WordsByInitial():
"Holds initial letter and set and list of words"
def __init__(self, letter):
self.letter = letter
self.words = []
self.unique_words = set()
def append(self, word):
self.words.append(word)
self.unique_words.add(word)
def __str__(self):
return "<{}: {} {}>".format(self.letter,
self.unique_words,
self.words)
>>> a_words = WordsByInitial('a')
>>> a_words.append('ahora')
>>> a_words.append('adios')
>>> a_words.append('ahora')
>>> print(a_words)
<a: {'adios', 'ahora'} ['ahora', 'adios', 'ahora']>
How  do  we  get  the  le2er?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
24. What  if  we  want  the  default_factory Â
to  receive  the  missing  key?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
25. class WordsDict(dict):
def __missing__(self, key):
res = self[key] = WordsByInitial(key)
return res
initials = WordsDict()
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y <y: {'y'} ['y', 'y', 'y', 'y', 'y', 'y']>
s <s: {'serĂa', 'sus', 'set).', 'sabe', 'sw', 'su'} ['susâ...
r <r: {'r&d', 'repasaremos'} ['repasaremos', 'r&d']>
q <q: {'quedarĂa', 'que'} ['que', 'que', 'quedarĂa', 'queâ...
...
Time  to  code  our  custom  dict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
26. class WordsDict(dict):
def __missing__(self, key):
res = self[key] = WordsByInitial(key)
return res
initials = WordsDict()
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y <y: {'y'} ['y', 'y', 'y', 'y', 'y', 'y']>
s <s: {'serĂa', 'sus', 'set).', 'sabe', 'sw', 'su'} ['susâ...
r <r: {'r&d', 'repasaremos'} ['repasaremos', 'r&d']>
q <q: {'quedarĂa', 'que'} ['que', 'que', 'quedarĂa', 'queâ...
...
Subclass  overriding  __missing__
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
27. Let'Ęšs  move  on  to  something  diďŹerent
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
28. from collections import defaultdict
def wordcount(s):
wc = defaultdict(int)
for word in s.split():
wc[word] += 1
return wc
>>> wc = wordcount(txt)
>>> for letter, num in wc.items():
print(letter, num)
del 1
implementar 1
exclusivamente 1
mĂĄs 4
y 6
...
>>> sorted(wc.items(), reverse=True, key=lambda x: x[1])[:3]
[('y', 6), ('de', 5), ('mĂĄs', 4)]
Letâs  count  words
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
29. from collections import Counter
def wordcount(s):
return Counter(s.split())
>>> wc = wordcount(txt)
>>> for letter, num in wc.items():
print(letter, num)
del 1
implementar 1
exclusivamente 1
mĂĄs 4
y 6
...
>>> wc.most_common(3)
[('y', 6), ('de', 5), ('mĂĄs', 4)]
collections.Counter
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
30. from collections import Counter
def wordcount(s):
return Counter(s.split())
>>> wc = wordcount(txt)
>>> for letter, num in wc.items():
print(letter, num)
del 1
implementar 1
exclusivamente 1
mĂĄs 4
y 6
...
>>> wc.most_common(3)
[('y', 6), ('de', 5), ('mĂĄs', 4)]
collections.Counter
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
31. >>> c1 = Counter(a=3, e=2, i=-1, o=5)
>>> c2 = Counter(a=1, b=1, c=1, d=1, e=1)
>>> c1['u']
0
>>> c1.most_common(2)
[('o', 5), ('a', 3)]
>>> list(c1.elements())
['o', 'o', 'o', 'o', 'o', 'a', 'a', 'a', 'e', 'e']
>>> c1.subtract(c2)
>>> c1
Counter({'o': 5, 'a': 2, 'e': 1, 'c': -1, 'b': -1, 'd': -1, 'i': -1})
>>> c1.update(['b', 'c', 'd'])
>>> c1
Counter({'o': 5, 'a': 2, 'e': 1, 'c': 0, 'b': 0, 'd': 0, 'i': -1})
More  on  collections.Counter
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
32. >>> c1 = Counter(a=3,
>>> c2 = Counter(a=1,
>>> c1 + c2
Counter({'o': 5, 'a':
>>> c1 - c2
Counter({'o': 5, 'a':
>>> c1 & c2
Counter({'a': 1, 'e':
>>> c1 | c2
Counter({'o': 5, 'a':
>>> +c1
Counter({'o': 5, 'a':
>>> -c1
Counter({'i': 1})
e=2, i=-1, o=5)
b=1, c=1, d=1, e=1)
3, 'e': 2, 'c': 1, 'b': 1, 'd': 1})
1})
1})
2, 'c': 1, 'b': 1, 'e': 1, 'd': 1})
2, 'e': 1})
More  on  collections.Counter
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
33. >âŻ
Counter  is  a  dict  subclass  for  counting  hashable  objects
>âŻ
dict  interface  but  they  return  0  instead  of  KeyError
>âŻ
Three  additional  methods:  most_common,  elements, Â
subtract
>âŻ
update  method  has  been  overriden
>âŻ
Support  for  mathematical  operators:  +,  -Âââ,  &,  |
>âŻ
Since  Python  2.7
collections.Counter
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
34. Let'Ęšs  go  back  to  words  classiďŹcation
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
35. from collections import defaultdict
>>> initials = defaultdict(list)
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y ['y', 'y', 'y', 'y', 'y', 'y']
s ['sus', 'set).', 'sabe', 'su', 'serĂa', 'su', 'sw']
r ['repasaremos', 'r&d']
q ['que', 'que', 'quedarĂa', 'que', 'que']
...
Classify  words  with  defaultdict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
36. What  if  we  only  want  to  keep  the
last  three  words  for  each  le2er?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
37. from collections import defaultdict, deque
>>> initials = defaultdict(lambda: deque(maxlen=3))
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y deque(['y', 'y', 'y'], maxlen=3)
s deque(['serĂa', 'su', 'sw'], maxlen=3)
r deque(['repasaremos', 'r&d'], maxlen=3)
q deque(['quedarĂa', 'que', 'que'], maxlen=3)
...
collections.deque
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
38. from collections import defaultdict, deque
>>> initials = defaultdict(lambda: deque(maxlen=3))
def classify_words(text):
for word in text.split():
word = word.lower()
initials[word[0]].append(word)
for letter, letter_words in initials.items():
print(letter, letter_words)
>>> classify_words(txt)
y deque(['y', 'y', 'y'], maxlen=3)
s deque(['serĂa', 'su', 'sw'], maxlen=3)
r deque(['repasaremos', 'r&d'], maxlen=3)
q deque(['quedarĂa', 'que', 'que'], maxlen=3)
...
collections.deque
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
39. >>> d = deque(maxlen=5)
>>> d.extend(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
>>> d
deque(['d', 'e', 'f', 'g', 'h'], maxlen=5)
>>> d.append('i')
>>> d
deque(['e', 'f', 'g', 'h', 'i'], maxlen=5)
>>> d.appendleft('Z')
>>> d
deque(['Z', 'e', 'f', 'g', 'h'], maxlen=5)
>>> d.rotate(3)
>>> d
deque(['f', 'g', 'h', 'Z', 'e'], maxlen=5)
>>> d.popleft()
'fâ
>>> d
deque(['g', 'h', 'Z', 'e'], maxlen=5)
More  on  collections.deque
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
41. OPERATION
AVERAGE
AMORTIZED Â WORST
append('bâ)
O(1)*
O(1)*
insert(index, 'bâ)
O(n)
O(n)
Get  item   d[4]
O(1)
O(1)
Set  item    d[4] = 'd'
O(1)
O(1)
Delete  item   del d[4]
O(n)
O(n)
extend(iterable)
O(k)*
O(k)*
Check  item   'b' in list
O(n)
O(n)
O(n  log  n)
O(n  log  n)
Sort
>âŻ
Represented  internally  as  an  array
>âŻ
*:  Amortized  cost.  Individual  ops  may  be  really  slow
>âŻ
Ideal  to  implement  stacks  (LIFO)
list  performance
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
42. Letâs  move  to  a  diďŹerent  example
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
43. CACHE = {}
def set_key(key, value):
"Set a key value"
CACHE[key] = value
def get_key(key):
"Retrieve a key value from the cache, or None if not found"
return CACHE.get(key, None)
>>> set_key("my_key", "the_valueâ)
>>> print(get_key("my_key"))
the_value
>>> print(get_key("not_found_key"))
None
Letâs  implement  a  SW  cache
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
44. What  if  we  want  to  limit  its  size?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
45. from collections import OrderedDict
CACHE = OrderedDict()
MAX_SIZE = 3
def set_key(key, value):
"Set a key value, removing oldest key if MAX_SIZE exceeded"
CACHE[key] = value
if len(CACHE) > MAX_SIZE:
CACHE.popitem(last=False)
def get_key(key):
"Retrieve a key value from the cache, or None if not found"
return CACHE.get(key, None)
>>> set_key("my_key", "the_valueâ)
>>> print(get_key("my_key"))
the_value
>>> print(get_key("not_found_key"))
None
>>> CACHE
OrderedDict([('c', 3), ('d', 4), ('e', 5)])
collections.OrderedDict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
46. from collections import OrderedDict
CACHE = OrderedDict()
MAX_SIZE = 3
def set_key(key, value):
"Set a key value, removing oldest key if MAX_SIZE exceeded"
CACHE[key] = value
if len(CACHE) > MAX_SIZE:
CACHE.popitem(last=False)
def get_key(key):
"Retrieve a key value from the cache, or None if not found"
return CACHE.get(key, None)
>>> set_key("my_key", "the_valueâ)
>>> print(get_key("my_key"))
the_value
>>> print(get_key("not_found_key"))
None
>>> CACHE
OrderedDict([('c', 3), ('d', 4), ('e', 5)])
collections.OrderedDict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
47. >>> d = OrderedDict()
>>> d.update([(âa', 1), (âe', 2), ('i', 3), ('o', 4), ('u', 5)])
>>> d
OrderedDict([('a', 1), ('e', 2), ('i', 3), ('o', 4), ('u', 5)])
>>> d['i'] = 0
>>> list(d.items())
[('a', 1), ('e', 2), ('i', 0), ('o', 4), ('u', 5)]
>>> d.popitem()
('u', 5)
>>> d.popitem(last=False)
('a', 1)
>>> d
OrderedDict([('e', 2), ('i', 0), ('o', 4)])
>>> d.move_to_end('i')
>>> d
OrderedDict([('e', 2), ('o', 4), ('i', 0)])
>>> d.move_to_end('i', last=False)
>>> d
OrderedDict([('i', 0), ('e', 2), ('o', 4)])
>>> d == OrderedDict([('e', 1), ('i', 2), ('o', 3)])
False
collections.OrderedDict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
48. >âŻ
OrderedDict  is  a  subclass  of  the  built-Âââin  dict  class
>âŻ
Remembers  the  order  that  keys  were  ďŹrst  inserted
>âŻ
Updating  a  key  does  not  modify  its  order
>âŻ
Two  additional  methods:  popitem,  move_to_end
>âŻ
Also  supports  reverse  iteration  using  reversed
>âŻ
Since  Python  2.7
collections.OrderedDict
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
49. And  ďŹnally,  one  last  example
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
50. class Color:
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
class Image:
def __init__(self, w, h, pixels):
self.w = w
self.h = h
self.pixels = pixels
def rotate(self):
pass
>>> pixels = [Color(127, 127, 127),
Color(127, 100, 100),
Color(127, 75, 75), ]
>>> picture = Image(1280, 720, pixels)
Letâs  implement  an  image
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
51. class Color:
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
class Image:
def __init__(self, w, h, pixels):
self.w = w
self.h = h
self.pixels = pixels
def rotate(self):
pass
>>> pixels = [Color(127, 127, 127),
Color(127, 100, 100),
Color(127, 75, 75), ]
>>> picture = Image(1280, 720, pixels)
Do  we  really  need  a  class?
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
52. >>> from collections import namedtuple
>>> Color = namedtuple('Color', ['r', 'g', 'b'])
class Image:
def __init__(self, w, h, pixels):
self.w = w
self.h = h
self.pixels = pixels
def rotate(self):
pass
>>> pixels = [Color(127, 127, 127),
Color(127, 100, 100),
Color(127, 75, 75), ]
>>> picture = Image(1280, 720, pixels)
>>> p = Color(127, 75, 25)
>>> p[1]
75
>>> p.b
25
collections.namedtuple
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
53. >>> from collections import namedtuple
>>> Color = namedtuple('Color', ['r', 'g', 'b'])
class Image:
def __init__(self, w, h, pixels):
self.w = w
self.h = h
self.pixels = pixels
def rotate(self):
pass
>>> pixels = [Color(127, 127, 127),
Color(127, 100, 100),
Color(127, 75, 75), ]
>>> picture = Image(1280, 720, pixels)
>>> p = Color(127, 75, 25)
>>> p[1]
75
>>> p.b
25
collections.namedtuple
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}
55. Thanks  for  coming!
Slides:
Q&A
{  âeventâ:  âPyCon  ES  2013â,  âauthorâ:  âPablo  Enfedaqueâ,  âtwi2erâ:  âpablitoev56â}