"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"             | dict subclass for counting *hashable* objects                        |
+-----------------------+----------------------------------------------------------------------+
| "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"                                                  |
+-----------------------+----------------------------------------------------------------------+


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, **kwargs)

      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)". Si des arguments sont passés par mot-
      clé, ils sont insérés comme de nouvelles entrées du dictionnaire
      ajouté. 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".

      Modifié dans la version 3.10: prise en charge des arguments par
      mot-clé.

   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:])".

   Notez que l'itération de "ChainMap()" se fait en parcourant les
   tableaux de correspondances du dernier jusqu'au premier :

      >>> baseline = {'music': 'bach', 'art': 'rembrandt'}
      >>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}
      >>> list(ChainMap(adjustments, baseline))
      ['music', 'art', 'opera']

   Cela produit le même ordre qu'une suite d'appels à "dict.update()"
   en commençant par le dernier tableau de correspondances :

      >>> combined = baseline.copy()
      >>> combined.update(adjustments)
      >>> list(combined)
      ['music', 'art', 'opera']

   Modifié dans la version 3.9: Ajout de la gestion des opérateurs "|"
   et "|=" tels que définis dans **PEP 584**.

Voir aussi:

  * La classe MultiContext dans le package CodeTools d'Enthought
    possède des options pour gérer l'écriture dans n'importe quel
    dictionnaire de la chaîne.

  * La classe de contexte de Django pour la création de modèles est
    une chaîne de dictionnaires en lecture seule. Elle comporte
    également des fonctionnalités d'ajouts et de retraits de contextes
    similaires à la méthode "new_child()" et à la propriété "parents".

  * Le Cas pratique des contextes imbriqués a des options pour
    contrôler si les écritures et autres mutations ne s'appliquent
    qu'au premier ou à un autre dictionnaire de la chaîne.

  * Une version grandement simplifiée de Chainmap en lecture seule.


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 is not None}

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

Exemple 3 : modèles pour simuler des contextes 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'] = 1            # Set value in current context
   d['x']                # Get first key in the chain of contexts
   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'})


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])

   A "Counter" is a "dict" subclass for counting *hashable* objects.
   It is a collection where elements are stored as dictionary keys and
   their counts are stored as dictionary values.  Counts are allowed
   to be any integer value including zero or negative counts.  The
   "Counter" class is similar to bags or multisets in other languages.

   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.

   Modifié dans la version 3.7: As a "dict" subclass, "Counter"
   inherited the capability to remember insertion order.  Math
   operations on *Counter* objects also preserve order.  Results are
   ordered according to when an element is first encountered in the
   left operand and then by the order encountered in the right
   operand.

   Counter objects support additional methods beyond those available
   for all dictionaries:

   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 l'ordre selon lequel ils sont rencontrés pour la
      première fois. 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])

      Renvoie une liste des *n* éléments les plus nombreux et leur
      valeur respective dans l'ordre décroissant. Si *n* n'est pas
      fourni ou vaut "None", "most_common()" renvoie *tous* les
      éléments du compteur. Les éléments qui ont le même nombre
      d'occurrences sont ordonnés par l'ordre selon lequel ils ont été
      rencontrés pour la première fois :

      >>> Counter('abracadabra').most_common(3)
      [('a', 5), ('b', 2), ('r', 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.

   total()

      Calcule la somme totale des nombres d'occurrences.

      >>> c = Counter(a=10, b=5, c=0)
      >>> c.total()
      15

      Nouveau dans la version 3.10.

   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)".

Les compteurs prennent en charge les comparaisons riches pour les
tests d'égalité ainsi que d'inclusion du membre gauche dans le membre
droite et réciproquement, avec les opérateurs "==", "!=", "<", "<=",
">" et ">=". Les éléments dont le nombre d'occurrences est à zéro sont
ignorés. Par exemple, on a "Counter(a=1) == Counter(a=1, b=0)".

Nouveau dans la version 3.10: ajout des comparaisons riches.

Modifié dans la version 3.10: les éléments dont le nombre
d'occurrences est à zéro sont désormais ignorés dans les tests
d'égalité. On avait auparavant "Counter(a=3) != Counter(a=3, b=0)".

Opérations usuelles sur les objets "Counter" :

   c.total()                       # 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

Several mathematical operations are provided for combining "Counter"
objects to produce multisets (counters that have counts greater than
zero). Addition and subtraction combine counters by adding or
subtracting the counts of corresponding elements.  Intersection and
union return the minimum and maximum of corresponding counts.
Equality and inclusion compare corresponding counts.  Each operation
can accept inputs with signed counts, but the output will exclude
results with counts of zero or less.

   >>> 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})
   >>> c == d                      # equality:  c[x] == d[x]
   False
   >>> c <= d                      # inclusion:  c[x] <= d[x]
   False

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.

  * La méthode "most_common()" exige uniquement que les valeurs soient
    ordonnables.

  * Les opérations de remplacement telles que "c[key] += 1" exigent
    une valeur dont le type gère l'addition et la soustraction. Cela
    inclut donc les fractions, les flottants et les décimaux, y
    compris négatifs. Il en va de même pour "update()" et
    "substract()" qui acceptent des valeurs négatives ou nulles dans
    les entrées et sorties.

  * 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.

  * La méthode "elements()" exige des valeurs entières et ignore les
    valeurs négatives ou nulles.

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*.

  * Pour lister tous les multiensembles distincts de même taille parmi
    un ensemble donné d'éléments, voir
    "itertools.combinations_with_replacement()" :

       map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC


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 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
   similaires 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 telles que "d[0]" pour accéder au premier
élément. 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'])


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
       # https://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

Un ordonnancement en round-robin peut être implémenté avec des entrées
itérateurs stockées dans une "deque". Les valeurs sont produites par
l'itérateur actif en position zéro. Si cet itérateur est épuisé, il
peut être retiré avec la méthode "popleft()" ; ou bien il peut être
remis à la fin avec la méthode "rotate()" :

   def roundrobin(*iterables):
       "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
       iterators = deque(map(iter, iterables))
       while iterators:
           try:
               while True:
                   yield next(iterators[0])
                   iterators.rotate(-1)
           except StopIteration:
               # Remove an exhausted iterator.
               iterators.popleft()

La méthode "rotate()" apporte une façon d'implémenter la sélection
d'intervalle (*slicing*) et les suppressions pour les "deques". Par
exemple, une implémentation de "del d[n]" en Python pur utilise la
méthode "rotate()" pour mettre en position les éléments à éjecter :

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

Pour implémenter le *slicing* pour les "deques", il est possible
d'utiliser une approche similaire en appliquant "rotate()" afin
d'apporter un élément cible à l'extrémité gauche de la *deque*. On
éjecte les anciennes entrées avec "popleft()" et on ajoute les
nouvelles avec "extend()", puis on inverse la rotation. Il est aisé
d'implémenter les manipulations des piles inspirées du Forth telles
que "dup", "drop", "swap", "over", "pick", "rot" et "roll".


Objets "defaultdict"
====================

class collections.defaultdict(default_factory=None, /[, ...])

   Renvoie un nouvel objet qui se comporte comme un dictionnaire.
   "defaultdict" est une sous-classe de 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".

   Modifié dans la version 3.9: ajout des opérateurs fusion ("|") et
   mise-à-jour ("|=") tels que définis dans **PEP 584**.


Exemples utilisant "defaultdict"
--------------------------------

Utiliser "list" comme "default_factory" facilite le regroupement d'une
séquence de paires clé-valeur en un dictionnaire de listes :

>>> 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])]

Lorsque chaque clé est rencontrée pour la première fois, elle n'est
pas encore présente dans le dictionnaire, donc une entrée est
automatiquement créée grâce à la fonction "default_factory" qui
renvoie un objet "list" vide. L'opération "list.append()" ajoute la
valeur à la nouvelle liste. Quand les clés sont à nouveau rencontrées,
la recherche se déroule correctement (elle renvoie la liste de cette
clé) et l'opération "list.append()" ajoute une autre valeur à la
liste. Cette technique est plus simple et plus rapide qu'une technique
équivalente utilisant "dict.setdefault()" :

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

Utiliser "int" comme "default_factory" rend la classe "defaultdict"
pratique pour le comptage (comme un sac ou multi-ensemble dans
d'autres langages) :

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

Quand une lettre est rencontrée pour la première fois, elle n'est pas
dans le dictionnaire, donc la fonction "default_factory" appelle
"int()" pour mettre un nouveau compteur à zéro. L'incrémentation
augmente ensuite le comptage pour chaque lettre.

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>'

Utiliser "set" comme "default_factory" rend la classe "defaultdict"
pratique pour créer un dictionnaire d'ensembles :

>>> 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})]


"namedtuple()" : fonction de construction pour *n*-uplets avec des champs nommés
================================================================================

Les *n*-uplets 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 *n*-uplets 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, *, rename=False, defaults=None, 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
   *n*-uplets 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 *n*-uplet 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".

   *defaults* peut être "None" ou un *iterable* de valeurs par défaut.
   Comme les champs avec une valeur par défaut doivent être définis
   après les champs sans valeur par défaut, les *defaults* sont
   appliqués aux paramètres les plus à droite. Par exemple, si les
   noms des champs sont "['x', 'y', 'z']" et les valeurs par défaut
   "(1, 2)", alors "x" est un argument obligatoire tandis que "y" et
   "y" valent par défaut "1" et "2".

   Si *module* est spécifié, alors il est assigné à l'attribut
   "__module__" du *n*-uplet nommé.

   Les instances de *n*-uplets 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 *n*-uplets natifs.

   Pour permettre la sérialisation, la classe de *n*-uplet nommée doit
   être assignée à une variable qui correspond à *typename*.

   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*.

   Modifié dans la version 3.7: Suppression du paramètre *verbose* et
   de l'attribut "_source".

   Modifié dans la version 3.7: Ajout du paramètre *defaults* et de
   l'attribut "_field_defaults".

   >>> # 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 *n*-uplets nommés sont particulièrement utiles pour associer des
noms de champs à des *n*-uplets 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 *n*-uplets 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 nouveau "dict" qui associe chaque nom de champ à sa
   valeur correspondante :

      >>> p = Point(x=11, y=22)
      >>> p._asdict()
      {'x': 11, 'y': 22}

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

   Modifié dans la version 3.8: renvoie un "dict" natif plutôt qu'un
   "OrderedDict". À partir de Python 3.7, les dictionnaires natifs
   garantissent la préservation de l'ordre. Si les autres
   fonctionnalités d'"OrderedDict" sont nécessaires, la solution
   préconisée est de convertir le résultat vers le type souhaité :
   "OrderedDict(nt._asdict())".

somenamedtuple._replace(**kwargs)

   Renvoie une nouvelle instance du *n*-uplet 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._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 *n*-uplets
   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)

somenamedtuple._field_defaults

   Dictionnaire qui assigne les valeurs par défaut aux noms des
   champs.

      >>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
      >>> Account._field_defaults
      {'balance': 0}
      >>> Account('premium')
      Account(type='premium', balance=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 *n*-uplet 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
*n*-uplets 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 *n*-uplet vide.
Cela permet de garder une emprunte mémoire faible en empêchant la
création de dictionnaire d'instance.

L'héritage n'est pas pertinent pour ajouter de nouveaux champs. Il est
préférable de simplement créer un nouveau type de *n*-uplet nommé avec
l'attribut "_fields" :

>>> 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.

Voir aussi:

  * Voir "typing.NamedTuple()" pour un moyen d'ajouter des indications
    de type pour les *n*-uplets nommés. Cela propose aussi une
    notation élégante utilisant le mot-clé "class" :

       class Component(NamedTuple):
           part_number: int
           weight: float
           description: Optional[str] = None

  * Voir "types.SimpleNamespace()" pour un espace de nommage muable
    basé sur un dictionnaire sous-jacent à la place d'un *n*-uplet.

  * Le module "dataclasses" fournit un décorateur et des fonctions
    pour ajouter automatiquement des méthodes spéciales générées aux
    classes définies par l’utilisateur.


Objets "OrderedDict"
====================

Les dictionnaires ordonnés sont des dictionnaires comme les autres
mais possèdent des capacités supplémentaires pour s'ordonner. Ils sont
maintenant moins importants puisque la classe native "dict" sait se
souvenir de l'ordre d'insertion (cette fonctionnalité a été garantie
par Python 3.7).

Quelques différences persistent vis-à-vis de "dict" :

* Les "dict" classiques ont été conçus pour être performants dans les
  opérations de correspondance. Garder une trace de l'ordre
  d'insertion était secondaire.

* Les "OrderedDict" ont été conçus pour être performants dans les
  opérations de ré-arrangement. L'occupation mémoire, la vitesse de
  parcours et les performances de mise à jour étaient secondaires.

* The "OrderedDict" algorithm can handle frequent reordering
  operations better than "dict".  As shown in the recipes below, this
  makes it suitable for implementing various kinds of LRU caches.

* Le test d'égalité de "OrderedDict" vérifie si l'ordre correspond.

  A regular "dict" can emulate the order sensitive equality test with
  "p == q and all(k1 == k2 for k1, k2 in zip(p, q))".

* La méthode "popitem()" de "OrderedDict" possède une signature
  différente. Elle accepte un argument optionnel pour spécifier quel
  élément doit être enlevé.

  A regular "dict" can emulate OrderedDict's "od.popitem(last=True)"
  with "d.popitem()" which is guaranteed to pop the rightmost (last)
  item.

  A regular "dict" can emulate OrderedDict's "od.popitem(last=False)"
  with "(k := next(iter(d)), d.pop(k))" which will return and remove
  the leftmost (first) item if it exists.

* "OrderedDict" possède une méthode "move_to_end()" pour déplacer
  efficacement un élément à la fin.

  A regular "dict" can emulate OrderedDict's "od.move_to_end(k,
  last=True)" with "d[k] = d.pop(k)" which will move the key and its
  associated value to the rightmost (last) position.

  A regular "dict" does not have an efficient equivalent for
  OrderedDict's "od.move_to_end(k, last=False)" which moves the key
  and its associated value to the leftmost (first) position.

* Avant Python 3.8, "dict" n'a pas de méthode "__reversed__()".

class collections.OrderedDict([items])

   Renvoie une instance d'une sous-classe de "dict" qui possède des
   méthodes spécialisées pour redéfinir l'ordre du dictionnaire.

   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 (last-in, first-out)) 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 (first-in, first-out)).

   move_to_end(key, last=True)

      Move an existing *key* to either end of an ordered dictionary.
      The item is moved to the right end if *last* is true (the
      default) or to the beginning if *last* is false.  Raises
      "KeyError" if the *key* does not exist:

         >>> d = OrderedDict.fromkeys('abcde')
         >>> d.move_to_end('b')
         >>> ''.join(d)
         'acdeb'
         >>> d.move_to_end('b', last=False)
         >>> ''.join(d)
         '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é.

Modifié dans la version 3.9: ajout des opérateurs fusion ("|") et
mise-à-jour ("|=") tels que définis dans **PEP 584**.


Exemples et cas pratiques utilisant "OrderDict"
-----------------------------------------------

Il est 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):
           super().__setitem__(key, value)
           self.move_to_end(key)

Un "OrderedDict" peut aussi être utile pour implémenter des variantes
de "functools.lru_cache()" :

   from time import time

   class TimeBoundedLRU:
       "LRU Cache that invalidates and refreshes old entries."

       def __init__(self, func, maxsize=128, maxage=30):
           self.cache = OrderedDict()      # { args : (timestamp, result)}
           self.func = func
           self.maxsize = maxsize
           self.maxage = maxage

       def __call__(self, *args):
           if args in self.cache:
               self.cache.move_to_end(args)
               timestamp, result = self.cache[args]
               if time() - timestamp <= self.maxage:
                   return result
           result = self.func(*args)
           self.cache[args] = time(), result
           if len(self.cache) > self.maxsize:
               self.cache.popitem(0)
           return result

   class MultiHitLRUCache:
       """ LRU cache that defers caching a result until
           it has been requested multiple times.

           To avoid flushing the LRU cache with one-time requests,
           we don't cache until a request has been made more than once.

       """

       def __init__(self, func, maxsize=128, maxrequests=4096, cache_after=1):
           self.requests = OrderedDict()   # { uncached_key : request_count }
           self.cache = OrderedDict()      # { cached_key : function_result }
           self.func = func
           self.maxrequests = maxrequests  # max number of uncached requests
           self.maxsize = maxsize          # max number of stored return values
           self.cache_after = cache_after

       def __call__(self, *args):
           if args in self.cache:
               self.cache.move_to_end(args)
               return self.cache[args]
           result = self.func(*args)
           self.requests[args] = self.requests.get(args, 0) + 1
           if self.requests[args] <= self.cache_after:
               self.requests.move_to_end(args)
               if len(self.requests) > self.maxrequests:
                   self.requests.popitem(0)
           else:
               self.requests.pop(args, None)
               self.cache[args] = result
               if len(self.cache) > self.maxsize:
                   self.cache.popitem(0)
           return result


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
   instances de "UserDict" fournissent l'attribut suivant :

   data

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


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-classes 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.


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(seq)

   Classe simulant une chaîne de caractères. Les instances de
   "UserString" possèdent un attribut "data" où est stocké leur
   contenu sous forme de chaîne de caractères native. Le contenu de
   l'instance est initialement une copie de *seq*, qui peut être
   n'importe quel objet convertible en chaîne de caractère avec la
   fonction native "str()".

   En plus de gérer les méthodes et opérations sur les chaînes de
   caractères, les instances de "UserString" possèdent l'attribut
   suivant :

   data

      Un objet "str" natif utilisé pour stocker le contenu de la
      classe "UserString".

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