Le namedtuple, la structure mal aimée


Figurez-vous qu’on a un chat IRC. Si, si ! freenode#sametmax administré par foxmask, qui est aussi le seul participant du chan.

Bon j’exagère, d’ailleurs tout à l’heure un lien de JEDI_BC m’a donné l’idée de cet article : qu’est-ce qu’un namedtuple et à quoi ça sert.

Les tuples, vous connaissez : c’est une séquence ordonnées non modifiable d’éléments hétérogènes.

Pardon, je recommence. C’est comme une liste, mais plus rapide, et pas modifiable. Ca se créer avec l’opérateur “,” :

>>> un_tuple = 1, 2, 3
>>> print(un_tuple)
(1, 2, 3)
>>> type(un_tuple)
<type 'tuple'>
>>> un_tuple[0]
1
>>> un_tuple[:-1]
(1, 2)
>>> autre_tuple = ("pomme", 1, True)

Les parenthèses sont optionnelles mais permettent d’éviter les ambiguïtés.

Bref, les tuples, c’est top : on peut les slicer, les indexer, et en plus c’est très très rapide car non modifiable :

>>> autre_tuple[0] = "banane"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Le tuple est donc une bonne alternative aux listes si on sait que le contenu ne bougera pas. Ca prend moins de mémoire, c’est plus rapide, c’est tout aussi flexible, et comme c’est un itérable on peut le plugger un peu partout.

Un gros défaut du tuple, néanmoins, c’est l’absence d’attributs nommés. Il faut connaitre les indices des valeurs, et c’est pas évident à lire si le tuple est gros.

Pour cette raison, la lib standard vient avec le namedtuple, qui est simplement un tuple dont les valeurs sont nommées.

Ca s’utilise en deux étapes. D’abord, définir un nouveau type de namedtuple avec la liste des attributs qu’il va contenir. Un peu comme définir une classe :

>>> from collections import namedtuple
>>> Joueur = namedtuple('Joueur', ['nom', 'age', 'signe_particulier'])

À partir de là nous avons une nouvelle “classe” de namedtuple appelée Joueur qui peut créer des namedtuples avec 3 attributs : ‘nom’, ‘age’ et ‘signe_particulier’.

Et ça s’utilise comme un tuple :

>>> jdg = Joueur('M. Grenier', 31, 'Un langage fleuri')
>>> jdg[0]
'M. Grenier'
>>> nom, age, signe = jdg
>>> nom
'M. Grenier'

Mais aussi comme un objet en read-only :

>>> jdg.nom
'M. Grenier'
>>> sam = Joueur(nom="Sam", age="Tu sauras pas", signe_particulier="Est parfaitement impartial")
>>> sam.age
'Tu sauras pas'

20 thoughts on “Le namedtuple, la structure mal aimée

  • Tiger-

    Petite coquille, dans le ème encadré de code :

    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: 'tuple' object does not support item assignment

    À la place du NameError ;)

  • Sam Post author

    Ce qui me fait peur, c’est que pour voir ce genre de détail, ça veut dire qu’il y a des lecteurs qui run les codes lignes à lignes. Tous les codes. Y compris les codes qui font des erreurs.

    Toutes mes erreurs sont vues.

    La pression je vous dis pas !

    Bon, merci en tout cas, c’est corrigé.

  • oliverpool

    Petite coquille sur le nom de la class : Joueur

    À partir de là nous avons une nouvelle “classe” de namedtuple appelée Joeur

    (oui, oui, on surveille les moindres caractères !)

  • JEDI_BC

    Met le lien que j’ai donné dans le post, ça peut intéresser quelques un

  • fpp

    Et contrairement à un tuple normal, les valeurs sont modifiables :

    sam._replace(signe_particulier="Est totalement partial")

  • kontre

    @fpp: un tuple modifiable, même named, je trouvais ça étrange alors j’ai regardé: _replace() ne modifie pas le tuple, mais renvoie une copie en changeant les valeurs passées en argument (comme string.replace()). Donc il faut faire :
    sam = sam._replace(signe_particulier="Est totalement partial")
    Les namedtuples sont bien non modifiables !

  • foxmask

    @sam : mouarf … mais alors .. tu testes pas le code de tes articles ? *:o)

  • kontre

    @fpp: Elle est fausse, si tu executes le code la dernière ligne lance une AssertionError. J’ai envoyé un mail au mec pour qu’il corrige ça.

  • herison

    bah y a pas d’image ? ^^

    namedtuple fabrique dynamiquement une classe qui encapsule un tuple. Je trouve que c’est à cheval entre 2 technique:
    1) J’utilise des classes pour avoir une couche d’abstraction propre
    2) Je fou toutes mes données dans des tuples qui contiennent des dicts qui contiennent des tuples et je fais un:
    voitures[ 'Md Michoue' ][5][3]['heure'][0]
    Pour savoir à quelle heure Md Michoue a prit sa voiture pour aller à la plage.

    étant (trop) souvent confronté a du legacy code de catégorie 2, je suis plutôt partisan de la 1er technique et je n’ai pas encore trouver le besoin d’utiliser les NamedTuple

  • Fred

    Hum, je ne connaissois point.
    En faisant un

    dir(jdg)

    , j’ai vu apparaitre les attributs “nom”, “age” et “signe_particulier”. Bon bref jdg est devenue en fait une classe.
    J’ai vu aussi d’autres attributs comme “count” et “index”. Et là, je me dis “hum, mais que se passera-donc-t-il-donc si j’écris

    Joueur = namedtuple('Joueur', ['count', 'index'])

    ??? Je pense que les attributs “count” et “index” vont écraser ceux définis par défaut. En tout cas, j’ai pas eu d’erreur donc danger dans les noms qu’on met aux “tuples nommés”…

    Sinon il y a moyen d’itérer les noms et leurs valeurs ? Un peu faire comme un

    jdg.iteritems()

    ???

  • Sam Post author

    Nope. Tu peux taper dans le mapping des attributs, mais dans ce cas autant utiliser un dico.

  • osef osef

    merci pour l’image, pas trop grave pour la couleur des cheveux :p

  • sympa ton article :)

    quelqu’un a fait un timeit ou un @wraps pour comparer un tuple() avec un named tuple() ou une liste ?

    (avec que je convertisse tout le dict de mon app temps réel ?)

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.