8.3. collections — Types de données de conteneurs

Code source : Lib/collections/__init__.py


Ce module implémente des types de données de conteneurs spécialisés qui apportent des alternatives aux conteneurs natifs de Python plus généraux dict, list, set et tuple.

namedtuple()

fonction permettant de créer des sous-classes de tuple avec des champs nommés

deque

conteneur se comportant comme une liste avec des ajouts et retraits rapides à chaque extrémité

ChainMap

classe semblable aux dictionnaires qui crée une unique vue à partir de plusieurs dictionnaires

Counter

sous-classe de dict pour compter des objets hachables

OrderedDict

sous-classe de dict qui garde en mémoire l’ordre dans lequel les entrées ont été ajoutées

defaultdict

sous-classe de dict qui appelle une fonction de fabrication en cas de valeur manquante

UserDict

surcouche autour des objets dictionnaires pour faciliter l’héritage de dict

UserList

surcouche autour des objets listes pour faciliter l’héritage de list

UserString

surcouche autour des objets chaînes de caractères pour faciliter l’héritage de str

Modifié dans la version 3.3: Moved Classes de base abstraites de collections to the collections.abc module. For backwards compatibility, they continue to be visible in this module as well.

8.3.1. Objets ChainMap

Nouveau dans la version 3.3.

Le module fournit une classe ChainMap afin de réunir rapidement plusieurs dictionnaires en une unique entité. Cela est souvent plus rapide que de créer un nouveau dictionnaire et d’effectuer plusieurs appels de update().

Cette classe peut être utilisée pour simuler des portées imbriquées, elle est aussi utile pour le templating.

class collections.ChainMap(*maps)

Un objet ChainMap regroupe plusieurs dictionnaires (ou autres tableaux de correspondance) en une vue que l’on peut mettre à jour. Si le paramètre maps est vide, un dictionnaire vide est fourni de telle manière qu’une nouvelle chaîne possède toujours au moins un dictionnaire.

Les dictionnaires sous-jacents sont stockés dans une liste. Celle-ci est publique et peut être consultée ou mise à jour via l’attribut maps. Il n’y a pas d’autre état.

Les recherches s’effectuent successivement dans chaque dictionnaire jusqu’à la première clé correspondante. En revanche, les écritures, mises à jour et suppressions n’affectent que le premier dictionnaire.

Un objet ChainMap incorpore les dictionnaires sous-jacents par leur référence. Ainsi, si l’un d’eux est modifié, les changements affectent également la ChainMap.

Toutes les méthodes usuelles des dictionnaires sont gérées. De plus, cette classe fournit un attribut maps, une méthode pour créer de nouveaux sous-contextes et une propriété pour accéder à tous les dictionnaires sous-jacents excepté le premier :

maps

Liste de dictionnaires éditable par l’utilisateur et classée selon l’ordre de recherche. Il s’agit de l’unique état stocké et elle peut être modifiée pour changer l’ordre de recherche. La liste doit toujours contenir au moins un dictionnaire.

new_child(m=None)

Renvoie un nouvel objet ChainMap contenant un nouveau dictionnaire suivi par tous les autres de l’instance actuelle. Si m est spécifié, il devient le nouveau dictionnaire au début de la liste ; sinon, un dictionnaire vide est utilisé, de telle manière qu’appeler d.new_child() équivaut à appeler ChainMap({}, *d.maps). Cette méthode est utile pour créer des sous-contextes qui peuvent être mis à jour sans altérer les valeurs dans les dictionnaires parents.

Modifié dans la version 3.4: Ajout du paramètre optionnel m.

parents

Propriété qui renvoie un nouvel objet ChainMap contenant tous les dictionnaires de l’instance actuelle hormis le premier. Cette propriété est utile pour ignorer le premier dictionnaire dans les recherches ; son utilisation rappelle le mot-clé nonlocal (utilisé pour les portées imbriquées), ou bien la fonction native super(). Une référence à d.parents est équivalente à : ChainMap(*d.maps[1:]).

Voir aussi

8.3.1.1. Exemples et cas pratiques utilisant ChainMap

Cette partie montre diverses approches afin de travailler avec les dictionnaires chaînés.

Exemple 1 : simulation de la chaîne de recherche interne de Python :

import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))

Exemple 2 : spécification d’une hiérarchie pour les options : ligne de commande, variable d’environnement, valeurs par défaut :

import os, argparse

defaults = {'color': 'red', 'user': 'guest'}

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k:v for k, v in vars(namespace).items() if v}

combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])

Exemple 3 : modèles pour simuler des contexte imbriqués avec la classe ChainMap :

c = ChainMap()        # Create root context
d = c.new_child()     # Create nested child context
e = c.new_child()     # Child of c, independent from d
e.maps[0]             # Current context dictionary -- like Python's locals()
e.maps[-1]            # Root context -- like Python's globals()
e.parents             # Enclosing context chain -- like Python's nonlocals

d['x']                # Get first key in the chain of contexts
d['x'] = 1            # Set value in current context
del d['x']            # Delete from current context
list(d)               # All nested values
k in d                # Check all nested values
len(d)                # Number of nested values
d.items()             # All nested items
dict(d)               # Flatten into a regular dictionary

La classe ChainMap ne met à jour (écriture et suppression) que le premier dictionnaire de la chaîne, alors qu’une recherche inspecte toute la chaîne. Cependant, si l’on veut effectuer des écritures ou suppressions en profondeur, on peut facilement faire une sous-classe qui met à jour les clés trouvées de la chaîne en profondeur :

class DeepChainMap(ChainMap):
    'Variant of ChainMap that allows direct updates to inner scopes'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'         # update an existing key two levels down
>>> d['snake'] = 'red'           # new keys get added to the topmost dict
>>> del d['elephant']            # remove an existing key one level down
>>> d                            # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

8.3.2. Objets Counter

Ce module fournit un outil pour effectuer rapidement et facilement des dénombrements. Par exemple :

>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})

>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
 ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
class collections.Counter([iterable-or-mapping])

La classe Counter est une sous-classe de dict qui permet le dénombrement d’objets hachables. Il s’agit d’une collection non ordonnée dans laquelle les éléments sont stockés comme des clés de dictionnaire et leurs nombres d’occurrences respectifs comme leurs valeurs. Ceux-ci peuvent être des entiers relatifs (positifs, négatifs ou nuls). La classe Counter est similaire aux sacs ou aux multiensembles dans d’autres langages.

Les éléments sont comptés à partir d’un itérable ou initialisés à partir d’un autre dictionnaire (ou compteur) :

>>> c = Counter()                           # a new, empty counter
>>> c = Counter('gallahad')                 # a new counter from an iterable
>>> c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
>>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args

Les objets Counter ont une interface de dictionnaire, à l’exception près qu’ils renvoient zéro au lieu de lever une exception KeyError pour des éléments manquants :

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']                              # count of a missing element is zero
0

Mettre un comptage à zéro pour un élément ne le retire pas de l’objet Counter. Il faut utiliser del pour le supprimer complètement :

>>> c['sausage'] = 0                        # counter entry with a zero count
>>> del c['sausage']                        # del actually removes the entry

Nouveau dans la version 3.1.

En plus des méthodes disponibles pour tous les dictionnaires, les objets compteurs gèrent trois méthodes supplémentaires :

elements()

Renvoie un itérateur sur chaque élément en le répétant autant de fois que la valeur du compteur associé. Les éléments sont renvoyés dans un ordre arbitraire. Si le comptage d’un élément est strictement inférieur à 1, alors elements() l’ignore.

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common([n])

Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or None, most_common() returns all elements in the counter. Elements with equal counts are ordered arbitrarily:

>>> Counter('abracadabra').most_common(3)  
[('a', 5), ('r', 2), ('b', 2)]
subtract([iterable-or-mapping])

Les éléments sont soustraits à partir d’un itérable ou d’un autre dictionnaire (ou compteur). Cette méthode se comporte comme dict.update() mais soustrait les nombres d’occurrences au lieu de les remplacer. Les entrées et sorties peuvent être négatives ou nulles.

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

Nouveau dans la version 3.2.

Les méthodes usuelles des dictionnaires sont disponibles pour les objets Counter à l’exception de deux méthodes qui fonctionnent différemment pour les compteurs.

fromkeys(iterable)

Cette méthode de classe n’est pas implémentée pour les objets Counter.

update([iterable-or-mapping])

Les éléments sont comptés à partir d’un itérable ou ajoutés d’un autre dictionnaire (ou compteur). Cette méthode se comporte comme dict.update() mais additionne les nombres d’occurrences au lieu de les remplacer. De plus, l’itérable doit être une séquence d’éléments et non une séquence de paires (clé, valeur).

Opérations usuelles sur les objets Counter :

sum(c.values())                 # total of all counts
c.clear()                       # reset all counts
list(c)                         # list unique elements
set(c)                          # convert to a set
dict(c)                         # convert to a regular dictionary
c.items()                       # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1]       # n least common elements
+c                              # remove zero and negative counts

Quelques opérations mathématiques sont fournies pour combiner des objets Counter afin de créer des multiensembles (des compteurs dont les dénombrements des éléments sont strictement supérieurs à zéro). Les additions et soustractions combinent les compteurs en ajoutant ou retranchant les nombres d’occurrences des éléments correspondants. Les intersections et unions renvoient les minimums et maximums des comptages correspondants. Chaque opération peut accepter des entrées avec des comptages relatifs, mais la sortie exclut les résultats avec des comptages négatifs ou nuls.

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # add two counters together:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x]) 
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

L’addition et la soustraction unaires (avec un seul terme) sont des raccourcis pour respectivement additionner un compteur avec un compteur vide ou et pour retrancher un compteur d’un compteur vide.

>>> c = Counter(a=2, b=-4)
>>> +c
Counter({'a': 2})
>>> -c
Counter({'b': 4})

Nouveau dans la version 3.3: Ajout de la gestion des additions et soustractions unaires, et des remplacements dans les multiensembles.

Note

Les compteurs ont été conçus essentiellement pour fonctionner avec des entiers naturels pour représenter les dénombrements en cours ; cependant, les cas d’utilisation nécessitant d’autres types ou des valeurs négatives n’ont pas été écartés. Pour vous aider dans ces cas particuliers, cette section documente la plage minimale et les restrictions de type.

  • La classe Counter est elle-même une sous-classe de dictionnaire sans restriction particulière sur ces clés ou valeurs. Les valeurs ont vocation à être des nombres représentants des comptages, mais il est possible de stocker n’importe quel type de valeur.

  • The most_common() method requires only that the values be orderable.

  • For in-place operations such as c[key] += 1, the value type need only support addition and subtraction. So fractions, floats, and decimals would work and negative values are supported. The same is also true for update() and subtract() which allow negative and zero values for both inputs and outputs.

  • Les méthodes de multiensembles sont uniquement conçues pour les cas d’utilisation avec des valeurs positives. Les entrées peuvent contenir des valeurs négatives ou nulles, mais seules les sorties avec des valeurs positives sont créées. Il n’y a pas de restriction de type, mais les types des valeurs doivent gérer l’addition, la soustraction et la comparaison.

  • The elements() method requires integer counts. It ignores zero and negative counts.

Voir aussi

  • Bag class dans Smalltalk.

  • L’article Wikipédia sur les multiensembles sur Wikipédia (ou l’article en anglais).

  • Des guides et exemples à propos des multiensembles en C++.

  • Pour les opérations mathématiques sur les multiensembles et leurs applications, voir Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19.

  • To enumerate all distinct multisets of a given size over a given set of elements, see itertools.combinations_with_replacement():

    map(Counter, combinations_with_replacement(“ABC”, 2)) –> AA AB AC BB BC CC

8.3.3. Objets deque

class collections.deque([iterable[, maxlen]])

Renvoie un nouvel objet deque initialisé de gauche à droite (en utilisant append()) avec les données d”iterable. Si iterable n’est pas spécifié, alors la nouvelle deque est vide.

Les deques sont une généralisation des piles et des files (deque se prononce « dèque » et est l’abréviation de l’anglais double-ended queue) : il est possible d’ajouter et retirer des éléments par les deux bouts des deques. Celles-ci gèrent des ajouts et des retraits utilisables par de multiples fils d’exécution (thread-safe) et efficients du point de vue de la mémoire des deux côtés de la deque, avec approximativement la même performance en O(1) dans les deux sens.

Bien que les les objets list gèrent des opérations similaires, ils sont optimisés pour des opérations qui ne changent pas la taille de la liste. Les opérations pop(0) et insert(0, v) qui changent la taille et la position de la représentation des données sous-jacentes entraînent des coûts de déplacement de mémoire en O(n).

Si maxlen n’est pas spécifié ou vaut None, les deques peuvent atteindre une taille arbitraire. Sinon, la deque est limitée par cette taille maximale. Une fois que celle-ci est atteinte, un ajout d’un ou plusieurs éléments engendre la suppression du nombre correspondant d’éléments à l’autre extrémité de la deque. Les deques à longueur limitée apportent des fonctionnalités similaire au filtre tail d’Unix. Elles sont aussi utiles pour le suivi de transactions et autres lots de données où seule l’activité récente est intéressante.

Les objets deques gèrent les méthodes suivantes :

append(x)

Ajoute x à l’extrémité droite de la deque.

appendleft(x)

Ajoute x à l’extrémité gauche de la deque.

clear()

Supprime tous les éléments de la deque et la laisse avec une longueur de 0.

copy()

Crée une copie superficielle de la deque.

Nouveau dans la version 3.5.

count(x)

Compte le nombre d’éléments de la deque égaux à x.

Nouveau dans la version 3.2.

extend(iterable)

Étend la deque en ajoutant les éléments de l’itérable en argument à son extrémité droite.

extendleft(iterable)

Étend la deque en ajoutant les éléments d”iterable à son extrémité gauche. Dans ce cas, notez que la série d’ajouts inverse l’ordre des éléments de l’argument itérable.

index(x[, start[, stop]])

Renvoie la position de x dans la deque (à partir de start inclus et jusqu’à stop exclus). Renvoie la première correspondance ou lève ValueError si aucune n’est trouvée.

Nouveau dans la version 3.5.

insert(i, x)

Insère x dans la deque à la position i.

Si une insertion provoque un dépassement de la taille limitée d’une deque, alors elle lève une exception IndexError.

Nouveau dans la version 3.5.

pop()

Retire et renvoie un élément de l’extrémité droite de la deque. S’il n’y a aucun élément, lève une exception IndexError.

popleft()

Retire et renvoie un élément de l’extrémité gauche de la deque. S’il n’y a aucun élément, lève une exception IndexError.

remove(value)

Supprime la première occurrence de value. Si aucune occurrence n’est trouvée, lève une exception ValueError.

reverse()

Inverse le sens des éléments de la deque sans créer de copie et renvoie None.

Nouveau dans la version 3.2.

rotate(n=1)

Décale les éléments de la deque de n places vers la droite (le dernier élément revient au début). Si n est négatif, décale vers la gauche.

Quand la deque n’est pas vide, un décalage d’une place vers la droite équivaut à d.appendleft(d.pop()) et un décalage d’une place vers la gauche est équivalent à d.append(d.popleft()).

Les objets deques fournissent également un attribut en lecture seule :

maxlen

La taille maximale d’une deque, ou None si illimitée.

Nouveau dans la version 3.1.

En plus des méthodes précédentes, les deques gèrent l’itération, la sérialisation, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), le test d’appartenance avec l’opérateur in, et les références en indice comme d[-1]. L’accès par indice est en O(1) aux extrémités mais en O(n) au milieu. Pour des accès aléatoires rapides, il est préférable d’utiliser des listes.

Depuis la version 3.5, les deques gèrent __add__(), __mul__() et __imul__().

Exemple :

>>> from collections import deque
>>> d = deque('ghi')                 # make a new deque with three items
>>> for elem in d:                   # iterate over the deque's elements
...     print(elem.upper())
G
H
I

>>> d.append('j')                    # add a new entry to the right side
>>> d.appendleft('f')                # add a new entry to the left side
>>> d                                # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])

>>> d.pop()                          # return and remove the rightmost item
'j'
>>> d.popleft()                      # return and remove the leftmost item
'f'
>>> list(d)                          # list the contents of the deque
['g', 'h', 'i']
>>> d[0]                             # peek at leftmost item
'g'
>>> d[-1]                            # peek at rightmost item
'i'

>>> list(reversed(d))                # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d                         # search the deque
True
>>> d.extend('jkl')                  # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)                      # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)                     # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])

>>> deque(reversed(d))               # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()                        # empty the deque
>>> d.pop()                          # cannot pop from an empty deque
Traceback (most recent call last):
    File "<pyshell#6>", line 1, in -toplevel-
        d.pop()
IndexError: pop from an empty deque

>>> d.extendleft('abc')              # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])

8.3.3.1. Cas pratiques utilisant deque

Cette partie montre diverses approches afin de travailler avec les deques.

Les deques à taille limitée apportent une fonctionnalité similaire au filtre tail d’Unix :

def tail(filename, n=10):
    'Return the last n lines of a file'
    with open(filename) as f:
        return deque(f, n)

Une autre approche d’utilisation des deques est de maintenir une séquence d’éléments récemment ajoutés en les ajoutant à droite et en retirant les anciens par la gauche :

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

The rotate() method provides a way to implement deque slicing and deletion. For example, a pure Python implementation of del d[n] relies on the rotate() method to position elements to be popped:

def delete_nth(d, n):
    d.rotate(-n)
    d.popleft()
    d.rotate(n)

To implement deque slicing, use a similar approach applying rotate() to bring a target element to the left side of the deque. Remove old entries with popleft(), add new entries with extend(), and then reverse the rotation. With minor variations on that approach, it is easy to implement Forth style stack manipulations such as dup, drop, swap, over, pick, rot, and roll.

8.3.4. Objets defaultdict

class collections.defaultdict([default_factory[, ...]])

Renvoie un nouvel objet qui se comporte comme un dictionnaire. defaultdict est une sous-classe de la la classe native dict. Elle surcharge une méthode et ajoute une variable d’instance modifiable. Les autres fonctionnalités sont les mêmes que celles des objets dict et ne sont pas documentées ici.

Le premier argument fournit la valeur initiale de l’attribut default_factory qui doit être un objet appelable sans paramètre ou None, sa valeur par défaut. Tous les autres arguments sont traités comme si on les passait au constructeur de dict, y compris les arguments nommés.

En plus des opérations usuelles de dict, les objets defaultdict gèrent les méthodes supplémentaires suivantes :

__missing__(key)

Si l’attribut default_factory est None, lève une exception KeyError avec key comme argument.

Si default_fatory` ne vaut pas None, cet attribut est appelé sans argument pour fournir une valeur par défaut pour la key demandée. Cette valeur est insérée dans le dictionnaire avec pour clé key et est renvoyée.

Si appeler default_factory lève une exception, celle-ci est transmise inchangée.

Cette méthode est appelée par la méthode __getitem__() de la classe dict lorsque la clé demandée n’est pas trouvée. Ce qu’elle renvoie ou lève est alors renvoyé ou levé par __getitem__().

Remarquez que __missing__() n’est pas appelée pour les opérations autres que __getitem__(). Cela signifie que get() renvoie None comme les dictionnaires natifs dans les cas triviaux et n’utilise pas default_factory.

Les objets defaultdict gèrent la variable d’instance :

default_factory

Cet attribut est utilisé par la méthode __missing__() ; il est initialisé par le premier argument passé au constructeur, s’il est spécifié, sinon par None.

8.3.4.1. Exemples utilisant defaultdict

Using list as the default_factory, it is easy to group a sequence of key-value pairs into a dictionary of lists:

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

When each key is encountered for the first time, it is not already in the mapping; so an entry is automatically created using the default_factory function which returns an empty list. The list.append() operation then attaches the value to the new list. When keys are encountered again, the look-up proceeds normally (returning the list for that key) and the list.append() operation adds another value to the list. This technique is simpler and faster than an equivalent technique using dict.setdefault():

>>> d = {}
>>> for k, v in s:
...     d.setdefault(k, []).append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

Setting the default_factory to int makes the defaultdict useful for counting (like a bag or multiset in other languages):

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]

When a letter is first encountered, it is missing from the mapping, so the default_factory function calls int() to supply a default count of zero. The increment operation then builds up the count for each letter.

La fonction int() qui retourne toujours zéro est simplement une fonction constante particulière. Un moyen plus flexible et rapide de créer une fonction constante est d’utiliser une fonction lambda qui peut fournir n’importe quelle valeur constante (pas seulement zéro) :

>>> def constant_factory(value):
...     return lambda: value
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'

Setting the default_factory to set makes the defaultdict useful for building a dictionary of sets:

>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
...     d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]

8.3.5. namedtuple(): fonction de construction pour n-uplets (tuples) avec des champs nommés

Les tuples nommés assignent une signification à chacun de leur élément, ce qui rend le code plus lisible et explicite. Ils peuvent être utilisés partout où les tuples natifs sont utilisés, et ils ajoutent la possibilité d’accéder à leurs champs grâce à leur nom au lieu de leur index de position.

collections.namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)

Renvoie une nouvelle sous-classe de tuple appelée typename. Elle est utilisée pour créer des objets se comportant comme les tuples qui ont des champs accessibles par recherche d’attribut en plus d’être indexables et itérables. Les instances de cette sous-classe possèdent aussi une docstring explicite (avec type_name et les field_names) et une méthode __repr__() pratique qui liste le contenu du tuple au format nom=valeur.

field_names peut être une séquence de chaînes de caractères telle que ['x', 'y'] ou bien une unique chaîne de caractères où les noms de champs sont séparés par un espace et/ou une virgule, par exemple 'x y' ou 'x, y'.

N’importe quel identifiant Python peut être utilisé pour un nom de champ hormis ceux commençant par un tiret bas. Les identifiants valides peuvent contenir des lettres, des chiffres (sauf en première position) et des tirets bas (sauf en première position). Un identifiant ne peut pas être un keyword tel que class, for, return, global, pass ou raise.

Si rename vaut True, alors les noms de champs invalides sont automatiquement renommés en noms positionnels. Par exemple, ['abc', 'def', 'ghi', 'abc'] est converti en ['abc, '_1', 'ghi', '_3'] afin d’éliminer le mot-clé def et le doublon de abc.

If verbose is true, the class definition is printed after it is built. This option is outdated; instead, it is simpler to print the _source attribute.

Si module est spécifié, alors il est assigné à l’attribut __module__ du tuple nommé.

Les instances de tuples nommés n’ont pas de dictionnaires propres, elles sont donc légères et ne requièrent pas plus de mémoire que les tuples natifs.

Modifié dans la version 3.1: Gestion de rename.

Modifié dans la version 3.6: Les paramètres verbose et rename deviennent des arguments obligatoirement nommés.

Modifié dans la version 3.6: Ajout du paramètre module.

>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

Les tuples nommés sont particulièrement utiles pour associer des noms de champs à des tuples renvoyés par les modules csv ou sqlite3 :

EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
    print(emp.name, emp.title)

import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
    print(emp.name, emp.title)

En plus des méthodes héritées de tuple, les tuples nommés implémentent trois méthodes et deux attributs supplémentaires. Pour éviter les conflits avec noms de champs, leurs noms commencent par un tiret bas.

classmethod somenamedtuple._make(iterable)

Méthode de classe qui construit une nouvelle instance à partir d’une séquence ou d’un itérable existant.

>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
somenamedtuple._asdict()

Renvoie un nouvel OrderedDict qui associe chaque nom de champ à sa valeur correspondante :

>>> p = Point(x=11, y=22)
>>> p._asdict()
OrderedDict([('x', 11), ('y', 22)])

Modifié dans la version 3.1: Renvoie un OrderedDict au lieu d’un dict natif.

somenamedtuple._replace(**kwargs)

Renvoie une nouvelle instance du tuple nommé en remplaçant les champs spécifiés par leurs nouvelles valeurs :

>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)

>>> for partnum, record in inventory.items():
...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
somenamedtuple._source

A string with the pure Python source code used to create the named tuple class. The source makes the named tuple self-documenting. It can be printed, executed using exec(), or saved to a file and imported.

Nouveau dans la version 3.3.

somenamedtuple._fields

Tuple de chaînes de caractères listant les noms de champs. Pratique pour l’introspection et pour créer de nouveaux types de tuples nommés à partir d’existants.

>>> p._fields            # view the field names
('x', 'y')

>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)

Pour récupérer un champ dont le nom est une chaîne de caractères, utilisez la fonction getattr() :

>>> getattr(p, 'x')
11

Pour convertir un dictionnaire en tuple nommé, utilisez l’opérateur double-étoile (comme expliqué dans Séparation des listes d’arguments) :

>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)

Il est aisé d’ajouter ou de modifier les fonctionnalités des tuples nommés grâce à l’héritage puisqu’il s’agit de simples classes. Voici comment ajouter un champ calculé avec une longueur fixe d’affichage :

>>> class Point(namedtuple('Point', ['x', 'y'])):
...     __slots__ = ()
...     @property
...     def hypot(self):
...         return (self.x ** 2 + self.y ** 2) ** 0.5
...     def __str__(self):
...         return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

>>> for p in Point(3, 4), Point(14, 5/7):
...     print(p)
Point: x= 3.000  y= 4.000  hypot= 5.000
Point: x=14.000  y= 0.714  hypot=14.018

La sous-classe ci-dessus définit __slots__ comme un tuple vide. Cela permet de garder une emprunte mémoire faible en empêchant la création de dictionnaire d’instance.

Subclassing is not useful for adding new, stored fields. Instead, simply create a new named tuple type from the _fields attribute:

>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))

Les docstrings peuvent être personnalisées en modifiant directement l’attribut __doc__ :

>>> Book = namedtuple('Book', ['id', 'title', 'authors'])
>>> Book.__doc__ += ': Hardcover book in active collection'
>>> Book.id.__doc__ = '13-digit ISBN'
>>> Book.title.__doc__ = 'Title of first printing'
>>> Book.authors.__doc__ = 'List of authors sorted by last name'

Modifié dans la version 3.5: La propriété devient éditable.

Default values can be implemented by using _replace() to customize a prototype instance:

>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
>>> janes_account = default_account._replace(owner='Jane')

Voir aussi

8.3.6. Objets OrderedDict

En plus de se comporter comme des dictionnaires natifs, les dictionnaires ordonnés mémorisent l’ordre dans lequel les éléments ont été insérés. Quand on itère sur un dictionnaire ordonné, les éléments sont renvoyés dans l’ordre d’insertion des clés.

class collections.OrderedDict([items])

Renvoie une instance d’une sous-classe de dict qui gère les méthodes usuelles de dict. Un objet OrderedDict est un dictionnaire qui mémorise l’ordre d’insertion des clés. Si une nouvelle entrée en écrase une autre, sa position reste inchangé. Si une entrée est supprimée puis réinsérée, elle est placée en dernière position.

Nouveau dans la version 3.1.

popitem(last=True)

La méthode popitem() pour les dictionnaires ordonnés retire et renvoie une paire (clé, valeur). Les paires sont renvoyées comme pour une pile, c’est-à-dire dernier entré, premier sorti (en anglais LIFO) si last vaut True. Si last vaut False, alors les paires sont renvoyées comme pour une file, c’est-à-dire premier entré, premier sorti (en anglais FIFO).

move_to_end(key, last=True)

Déplace une clé key existante à l’une des deux extrémités du dictionnaire : à droite si last vaut True (comportement par défaut) ou à gauche sinon. Lève une exception KeyError si la clé key n’est pas trouvée :

>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d.keys())
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d.keys())
'bacde'

Nouveau dans la version 3.2.

En plus des méthodes usuelles des dictionnaires, les dictionnaires ordonnés gèrent l’itération en sens inverse grâce à reversed().

Les tests d’égalité entre deux objets OrderedDict sont sensibles à l’ordre et sont implémentés comme ceci : list(od1.items() == list(od2.items()). Les tests d’égalité entre un objet OrderedDict et un objet Mapping ne sont pas sensibles à l’ordre (comme les dictionnaires natifs). Cela permet substituer des objets OrderedDict partout où les dictionnaires natifs sont utilisés.

Modifié dans la version 3.5: Les vues d’éléments, de clés et de valeurs de OrderedDict gèrent maintenant l’itération en sens inverse en utilisant reversed().

Modifié dans la version 3.6: Suite à l’acceptation de la PEP 468, l’ordre des arguments nommés passés au constructeur et à la méthode update() de OrderedDict est conservé.

8.3.6.1. Exemples et cas pratiques utilisant OrderDict

Puisqu’un dictionnaire ordonné mémorise l’ordre d’insertion de ses éléments, il peut être utilisé conjointement avec un classement pour créer un dictionnaire trié :

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

Les nouveaux dictionnaires triés gardent leur classement quand des entrées sont supprimées, mais si de nouvelles clés sont ajoutées, celles-ci sont ajoutée à la fin et le classement est perdu.

Il est également facile de créer une variante de dictionnaire ordonné qui retient l’ordre dans lequel les clés ont été insérées en dernier. Si une nouvelle entrée écrase une existante, la position d’insertion d’origine est modifiée et déplacée à la fin :

class LastUpdatedOrderedDict(OrderedDict):
    'Store items in the order the keys were last added'

    def __setitem__(self, key, value):
        if key in self:
            del self[key]
        OrderedDict.__setitem__(self, key, value)

Un dictionnaire ordonné peut être combiné avec la classe Counter afin de mémoriser l’ordre dans lequel les éléments ont été ajoutés pour la première fois :

class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

8.3.7. Objets UserDict

La classe UserDict se comporte comme une surcouche autour des dictionnaires. L’utilité de cette classe est réduite car on peut maintenant hériter directement de dict. Cependant, il peut être plus facile de travailler avec celle-ci car le dictionnaire sous-jacent est accessible comme attribut.

class collections.UserDict([initialdata])

Classe simulant un dictionnaire. Les instances de UserDict possèdent un attribut data où est stocké leur contenu sous forme de dictionnaire natif. Si initialdata est spécifié, alors data est initialisé avec son contenu. Remarquez qu’une référence vers initialdata n’est pas conservée, ce qui permet de l’utiliser pour d’autres tâches.

En plus de gérer les méthodes et opérations des dictionnaires, les instance de UserDict fournissent l’attribut suivant :

data

Un dictionnaire natif où est stocké le contenu de la classe UserDict.

8.3.8. Objets UserList

Cette classe agit comme une surcouche autour des objets list. C’est une classe mère utile pour vos classes listes-compatibles qui peuvent en hériter et surcharger les méthodes existantes ou en ajouter de nouvelles. Ainsi, on peut ajouter de nouveaux comportements aux listes.

L’utilité de cette classe a été partiellement réduite par la possibilité d’hériter directement de list. Cependant, il peut être plus facile de travailler avec cette classe car la liste sous-jacente est accessible via un attribut.

class collections.UserList([list])

Classe simulant une liste. Les instances de UserList possèdent un attribut UserList où est stocké leur contenu sous forme de liste native. Il est initialement une copie de list, ou [] par défaut. list peut être un itérable, par exemple une liste native ou un objet UserList.

En plus de gérer les méthodes et opérations des séquences muables, les instances de UserList possèdent l’attribut suivant :

data

Un objet list natif utilisé pour stocker le contenu de la classe UserList.

Prérequis pour l’héritage : Les sous-classe de UserList doivent implémenter un constructeur qui peut être appelé avec zéro ou un argument. Les opérations sur les listes qui renvoient une nouvelle séquence essayent de créer une instance de la classe courante. C’est pour cela que le constructeur doit pouvoir être appelé avec un unique paramètre, un objet séquence utilisé comme source de données.

Si une classe fille ne remplit pas cette condition, toutes les méthodes spéciales gérées par cette classe devront être implémentées à nouveau. Merci de consulter les sources pour obtenir des informations sur les méthodes qui doivent être fournies dans ce cas.

8.3.9. Objets UserString

La classe UserString agit comme une surcouche autour des objets str. L’utilité de cette classe a été partiellement réduite par la possibilité d’hériter directement de str. Cependant, il peut être plus facile de travailler avec cette classe car la chaîne de caractère sous-jacente est accessible via un attribut.

class collections.UserString([sequence])

Class that simulates a string or a Unicode string object. The instance’s content is kept in a regular string object, which is accessible via the data attribute of UserString instances. The instance’s contents are initially set to a copy of sequence. The sequence can be an instance of bytes, str, UserString (or a subclass) or an arbitrary sequence which can be converted into a string using the built-in str() function.

Modifié dans la version 3.5: Nouvelles méthodes __getnewargs__, __rmod__, casefold, format_map, isprintable et maketrans.