Écrire du code pythonique et propre avec namedtuple – Real Python

By | mai 12, 2021

python pour débutant

Python collections module fournit une fonction d'usine appelée namedtuple (), spécialement conçu pour rendre votre code plus Pythonique lorsque vous travaillez avec des tuples. Avec namedtuple (), vous pouvez créer des types de séquence immuables qui vous permettent d'accéder à leurs valeurs à l'aide de noms de champs descriptifs et notation par points au lieu d'indices entiers peu clairs.

Si vous avez une certaine expérience de l'utilisation de Python, vous savez que l'écriture de code Pythonic est une compétence de base pour les développeurs Python. Dans ce didacticiel, vous améliorerez cette compétence en utilisant namedtuple.

Dans ce didacticiel, vous apprendrez à:

  • Créer namedtuple classes utilisant namedtuple ()
  • Identifiez et profitez de fonctionnalités intéressantes de namedtuple
  • Utiliser namedtuple instances à écrire Code pythonique
  • Décidez si vous souhaitez utiliser un namedtuple ou un structure de données similaire
  • Sous-classe une namedtuple pour fournir de nouvelles fonctionnalités

Pour tirer le meilleur parti de ce didacticiel, vous devez avoir une compréhension générale de la philosophie de Python liée à l'écriture de code pythonique et lisible. Vous devez également connaître les bases du travail avec:

Si vous ne possédez pas toutes les connaissances requises avant de commencer ce didacticiel, c'est pas grave! Vous pouvez arrêter et revoir les ressources ci-dessus si nécessaire.

Utilisant namedtuple pour écrire du code pythonique

Python namedtuple () est une fonction d'usine disponible dans collections. Cela vous permet de créer tuple sous-classes avec champs nommés. Vous pouvez accéder aux valeurs d'un tuple nommé donné en utilisant le notation par points et les noms de champs, comme dans obj.attr.

Python namedtuple a été créé pour améliorer la lisibilité du code en fournissant un moyen d'accéder aux valeurs à l'aide de noms de champs descriptifs au lieu d'indices entiers, qui la plupart du temps ne fournissent aucun contexte sur les valeurs. Cette fonctionnalité rend également le code plus propre et plus facile à gérer.

En revanche, l'utilisation d'indices sur des valeurs dans un tuple régulier peut être ennuyeuse, difficile à lire et sujette aux erreurs. Cela est particulièrement vrai si le tuple a beaucoup de champs et est construit loin de l'endroit où vous l'utilisez.

Outre cette fonctionnalité principale des tuples nommés, vous découvrirez qu'ils:

  • Sont immuable structures de données
  • Avoir une valeur de hachage cohérente
  • Peut fonctionner comme clés du dictionnaire
  • Peut être stocké dans des ensembles
  • Avoir une docstring utile basée sur le type et les noms de champ
  • Fournir un représentation sous forme de chaîne qui imprime le contenu du tuple dans un nom = valeur format
  • Support indexage
  • Fournissez des méthodes et des attributs supplémentaires, tels que ._Fabriquer(), _asdict (), ._des champs, etc
  • Sont rétrocompatible avec des tuples réguliers
  • Avoir consommation de mémoire similaire aux tuples réguliers

En général, vous pouvez utiliser namedtuple instances partout où vous avez besoin d'un objet de type tuple. Les tuples nommés ont l'avantage de fournir un moyen d'accéder à leurs valeurs à l'aide des noms de champ et de la notation par points. Cela rendra votre code plus pythonique.

Avec cette brève introduction à namedtuple et ses fonctionnalités générales, vous pouvez approfondir leur création et leur utilisation dans votre code.

Création de classes de type tuple avec namedtuple ()

Vous utilisez un namedtuple () pour créer une structure de données immuable et semblable à un tuple avec des noms de champ. Un exemple populaire que vous trouverez dans les didacticiels sur namedtuple est de créer une classe pour représenter un point mathématique.

Selon le problème, vous souhaiterez probablement utiliser une structure de données immuable pour représenter un point donné. Voici comment créer un point bidimensionnel à l'aide d'un tuple régulier:

>>>

>>> # Créer un point 2D sous forme de tuple
>>> point = (2, 4)
>>> point
(2, 4)

>>> # Coordonnées d'accès x
>>> point[[[[0]
2
>>> # Accédez à la coordonnée y
>>> point[[[[1]
4

>>> # Essayez de mettre à jour une valeur de coordonnée
>>> point[[[[0] = 3
Traceback (dernier appel le plus récent):
  Déposer "", ligne 1, dans 
Erreur-type: L'objet 'tuple' ne prend pas en charge l'attribution d'éléments

Ici, vous créez un immuable bidimensionnel point en utilisant un régulier tuple. Ce code fonctionne: vous avez un point avec deux coordonnées, et vous ne pouvez modifier aucune de ces coordonnées. Cependant, ce code est-il lisible? Pouvez-vous dire à l'avance ce que 0 et 1 les indices signifient? Pour éviter ces ambiguïtés, vous pouvez utiliser un namedtuple comme ça:

>>>

>>> de collections importer namedtuple

>>> # Créer un type de multiplet nommé, Point
>>> Point = namedtuple("Point", "x y")
>>> isubclass(taper(point), tuple)
Vrai

>>> # Instancier le nouveau type
>>> point = Point(2, 4)
>>> point
Point (x = 2, y = 4)

>>> # Notation par points pour accéder aux coordonnées
>>> point.X
2
>>> point.y
4

>>> # Indexation pour accéder aux coordonnées
>>> point[[[[0]
2
>>> point[[[[1]
4

>>> # Les tuples nommés sont immuables
>>> point.X = 100
Traceback (dernier appel le plus récent):
  Déposer "", ligne 1, dans 
AttributeError: impossible de définir l'attribut

Maintenant vous avez un point avec deux champs correctement nommés, X et y. Votre point fournit une représentation sous forme de chaîne conviviale et descriptive (Point (x = 2, y = 4)) par défaut. Il vous permet d'accéder aux coordonnées en utilisant la notation par points, ce qui est pratique, lisible et explicite. Vous pouvez également utiliser des indices pour accéder à la valeur de chaque coordonnée.

Enfin, depuis namedtuple les classes sont des sous-classes de tuple, ils sont également immuables. Donc, si vous essayez de modifier la valeur d'une coordonnée, vous obtiendrez un AttributeError.

Fournir des arguments obligatoires à namedtuple ()

Comme vous l'avez appris auparavant, namedtuple () est une fonction d'usine plutôt qu'une structure de données typique. Pour créer un nouveau namedtuple, vous devez fournir deux arguments de position à la fonction:

  1. nom de type fournit le nom de classe pour le namedtuple retourné par namedtuple (). Vous devez passer une chaîne avec un identifiant Python valide à cet argument.
  2. field_names fournit les noms de champ que vous utiliserez pour accéder aux valeurs du tuple. Vous pouvez fournir les noms de champ en utilisant:
    • Un itérable de chaînes, comme ["field1", "field2", ..., "fieldN"]
    • Une chaîne avec chaque nom de champ séparé par un espace, tel que "champ1 champ2 ... champN"
    • Une chaîne avec chaque nom de champ séparé par des virgules, comme "champ1, champ2, ..., champN"

Pour illustrer comment fournir field_names, voici différentes manières de créer des points:

>>>

>>> de collections importer namedtuple

>>> # Une liste de chaînes pour les noms de champs
>>> Point = namedtuple("Point", [[[["X", "y"])
>>> Point

>>> Point(2, 4)
Point (x = 2, y = 4)

>>> # Une chaîne avec des noms de champs séparés par des virgules
>>> Point = namedtuple("Point", "x, y")
>>> Point

>>> Point(4, 8)
Point (x = 4, y = 8)

>>> # Une expression de générateur pour les noms de champs
>>> Point = namedtuple("Point", (domaine pour domaine dans "xy"))
>>> Point

>>> Point(8, 16)
Point (x = 8, y = 16)

Dans ces exemples, vous créez d'abord Point utilisant un liste des noms de champs. Ensuite, vous utilisez une chaîne avec des noms de champs séparés par des virgules. Enfin, vous utilisez une expression de générateur. Cette dernière option peut sembler exagérée dans cet exemple. Cependant, il vise à illustrer la flexibilité du processus.

Vous pouvez utiliser n'importe quel identifiant Python valide pour les noms de champ, sauf pour:

  • Noms commençant par un trait de soulignement (_)
  • Python mots clés

Si vous fournissez des noms de champ qui ne respectent pas l'une de ces conditions, vous obtenez un ValueError:

>>>

>>> de collections importer namedtuple

>>> Point = namedtuple("Point", [[[["X", "_y"])
Traceback (dernier appel le plus récent):
  ...
ValueError: Les noms de champ ne peuvent pas commencer par un trait de soulignement: «_y»

Dans cet exemple, le deuxième nom de champ commence par et un trait de soulignement, vous obtenez donc un ValueError vous disant que les noms de champ ne peuvent pas commencer par ce caractère. Ceci a pour but d'éviter les conflits de nom avec le namedtuple méthodes et attributs.

Dans le cas de nom de type, une question peut se poser lorsque vous regardez les exemples ci-dessus: Pourquoi dois-je fournir le nom de type argument? La réponse est que vous avez besoin d'un nom pour la classe renvoyée par namedtuple (). C'est comme créer un alias pour une classe existante:

>>>

>>> de collections importer namedtuple

>>> Point1 = namedtuple("Point", "x y")
>>> Point1


>>> classer Point:
...     def __init__(soi, X, y):
...         soi.X = X
...         soi.y = y
...

>>> Point2 = Point
>>> Point2

Dans le premier exemple, vous créez Point utilisant namedtuple (). Ensuite, vous affectez ce nouveau type à la variable globale Point1. Dans le deuxième exemple, vous créez une classe Python standard également nommée Point, puis vous attribuez la classe à Point2. Dans les deux cas, le nom de la classe est Point. Point1 et Point2 sont des alias pour la classe en question.

Enfin, vous pouvez également créer un tuple nommé en utilisant des arguments de mot-clé ou en fournissant un dictionnaire existant, comme ceci:

>>>

>>> de collections importer namedtuple

>>> Point = namedtuple("Point", "x y")

>>> Point(X=2, y=4)
Point (x = 2, y = 4)

>>> Point(**"X": 4, "y": 8)
Point (x = 4, y = 8)

Dans le premier exemple, vous utilisez des arguments de mot-clé pour créer un Point objet. Dans le deuxième exemple, vous utilisez un dictionnaire dont les clés correspondent aux champs de Point. Dans ce cas, vous devez effectuer un déballage du dictionnaire.

Utilisation d'arguments facultatifs avec namedtuple ()

Outre les deux arguments requis, le namedtuple () La fonction factory prend également les arguments optionnels suivants:

Si vous définissez Renommer à Vrai, tous les noms de champ non valides sont automatiquement remplacés par des noms de position.

Supposons que votre entreprise dispose d'une ancienne application de base de données, écrite en Python, pour gérer les données sur les passagers qui voyagent avec l'entreprise. Vous êtes invité à mettre à jour le système et vous commencez à créer des tuples nommés pour stocker les données que vous lisez dans la base de données.

L'application fournit une fonction appelée get_column_names () qui renvoie une liste de chaînes avec les noms de colonne, et vous pensez que vous pouvez utiliser cette fonction pour créer un namedtuple classer. Vous vous retrouvez avec le code suivant:

# passager.py

de collections importer namedtuple

de base de données importer get_column_names

Passager = namedtuple("Passager", get_column_names())

Cependant, lorsque vous exécutez le code, vous obtenez un suivi d'exception comme suit:

Traceback (dernier appel le plus récent):
  ...
ValueError: Les noms de type et les noms de champ ne peuvent pas être un mot-clé: "classe"

Cela vous indique que le classer nom de colonne n'est pas un nom de champ valide pour votre namedtuple classer. Pour éviter cette situation, vous décidez d'utiliser Renommer:

# passager.py

# ...

Passager = namedtuple("Passager", get_column_names(), Renommer=Vrai)

Ce qui provoque namedtuple () pour remplacer automatiquement les noms non valides par des noms de position. Supposons maintenant que vous récupériez une ligne de la base de données et créez votre premier Passager exemple, comme ceci:

>>>

>>> de passager importer Passager
>>> de base de données importer get_passenger_by_id

>>> Passager(get_passenger_by_id(«1234»))
Passager (_0 = 1234, name = 'john', _2 = 'Business', _3 = 'John Doe')

Dans ce cas, get_passenger_by_id () est une autre fonction disponible dans votre application hypothétique. Il récupère les données d'un passager donné dans un tuple. Le résultat final est que votre passager nouvellement créé a trois noms de champ de position, et seulement Nom reflète le nom de la colonne d'origine. Lorsque vous fouillez dans la base de données, vous vous rendez compte que la table des passagers contient les colonnes suivantes:

Colonne Magasins Remplacé? Raison
_identifiant Un identifiant unique pour chaque passager Oui Cela commence par un trait de soulignement.
Nom Un nom court pour chaque passager Non C'est un identifiant Python valide.
classer La classe dans laquelle le passager voyage Oui C'est un mot-clé Python.
Nom Le nom complet du passager Oui C'est répété.

Dans les situations où vous créez des tuples nommés basés sur des valeurs hors de votre contrôle, le Renommer l'option doit être définie sur Vrai ainsi les champs non valides sont renommés avec des noms de position valides.

Le deuxième argument facultatif de namedtuple () est valeurs par défaut. Cet argument est par défaut Rien, ce qui signifie que les champs n'auront pas de valeurs par défaut. Vous pouvez définir valeurs par défaut à un itérable de valeurs. Dans ce cas, namedtuple () affecte les valeurs dans le valeurs par défaut itérable dans les champs les plus à droite:

>>>

>>> de collections importer namedtuple

>>> Développeur = namedtuple(
...     "Développeur",
...     "langue au niveau du nom",
...     valeurs par défaut=[[[["Junior", "Python"]
... )

>>> Développeur("John")
Développeur (nom = 'John', level = 'Junior', language = 'Python')

Dans cet exemple, le niveau et Langue les champs ont des valeurs par défaut. Cela en fait des arguments optionnels. Étant donné que vous ne définissez pas de valeur par défaut pour Nom, vous devez fournir une valeur lorsque vous créez le namedtuple exemple. Ainsi, les arguments sans valeur par défaut sont requis. Notez que les valeurs par défaut sont appliquées aux champs les plus à droite.

Le dernier argument de namedtuple () est module. Si vous fournissez un nom de module valide à cet argument, alors le .__module__ attribut du résultat namedtuple est défini sur cette valeur. Cet attribut contient le nom du module dans lequel une fonction donnée ou appelable est définie:

>>>

>>> de collections importer namedtuple

>>> Point = namedtuple("Point", "x y", module="Douane")
>>> Point

>>> Point.__module__
'Douane'

Dans cet exemple, lorsque vous accédez .__module__ au Point, vous obtenez 'Douane' par conséquent. Cela indique que votre Point la classe est définie dans votre Douane module.

La motivation pour ajouter le module argument à namedtuple () dans Python 3.6 était de permettre aux tuples nommés de prendre en charge le décapage via différentes implémentations Python.

Explorer les fonctionnalités supplémentaires de namedtuple Des classes

Outre les méthodes héritées de tuple, tel que .compter() et .indice(), namedtuple Les classes fournissent également trois méthodes supplémentaires et deux attributs. Pour éviter les conflits de nom avec les champs personnalisés, les noms de ces attributs et méthodes commencent par un trait de soulignement. Dans cette section, vous découvrirez ces méthodes et attributs et leur fonctionnement.

Création namedtuple Instances d'Iterables

Vous pouvez utiliser ._Fabriquer() pour créer des instances de tuple nommé. La méthode prend un itérable de valeurs et retourne un nouveau tuple nommé:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge hauteur")
>>> Personne._Fabriquer([[[["Jeanne", 25, 1,75])
Personne (nom = 'Jane', âge = 25, taille = 1,75)

Ici, vous créez d'abord un Personne classe utilisant namedtuple (). Alors tu appelles ._Fabriquer() avec une liste de valeurs pour chaque champ dans le namedtuple. Noter que ._Fabriquer() est une méthode de classe qui fonctionne comme un constructeur de classe alternatif et renvoie une nouvelle instance de tuple nommé.

Pour terminer, ._Fabriquer() attend un seul itérable comme argument, un liste dans l'exemple ci-dessus. D'autre part, le namedtuple Le constructeur peut prendre des arguments positionnels ou des mots-clés, comme vous l'avez déjà appris.

Conversion namedtuple Instances dans les dictionnaires

Vous pouvez convertir des instances de tuple nommées existantes en dictionnaires à l'aide de ._asdict (). Cette méthode renvoie un nouveau dictionnaire qui utilise les noms de champ comme clés. Les clés du dictionnaire résultant sont dans le même ordre que les champs de l'original namedtuple:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge hauteur")
>>> Jeanne = Personne("Jeanne", 25, 1,75)
>>> Jeanne._asdict()
'name': 'Jane', 'age': 25, 'height': 1,75

Quand vous appelez ._asdict () sur un tuple nommé, vous obtenez un nouveau dict objet qui mappe les noms de champ à leurs valeurs correspondantes dans le tuple nommé d'origine.

Depuis Python 3.8, ._asdict () a renvoyé un dictionnaire normal. Avant cela, il retournait un OrdonnéDict objet:

>>>

Python 3.7.9 (par défaut, 14 janvier 2021, 11:41:20)
[GCC 9.3.0]    sur linux
Tapez "aide", "copyright", "crédits" ou "licence" pour plus d'informations.
>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge hauteur")
>>> Jeanne = Personne("Jeanne", 25, 1,75)
>>> Jeanne._asdict()
OrderedDict ([('name', 'Jane'), ('age', 25), ('height', 1.75)])

Python 3.8 mis à jour ._asdict () pour renvoyer un dictionnaire normal car les dictionnaires se souviennent de l'ordre d'insertion de leurs clés dans Python 3.6 et plus. Notez que l'ordre des clés dans le dictionnaire résultant est équivalent à l'ordre des champs dans le tuple nommé d'origine.

Remplacement des champs existants namedtuple Instances

La dernière méthode que vous découvrirez est ._remplacer(). Cette méthode prend des arguments de mot-clé de la forme champ = valeur et renvoie un nouveau namedtuple instance mettant à jour les valeurs des champs sélectionnés:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge hauteur")
>>> Jeanne = Personne("Jeanne", 25, 1,75)

>>> # Après l'anniversaire de Jane
>>> Jeanne = Jeanne._remplacer(âge=26)
>>> Jeanne
Personne (nom = 'Jane', âge = 26, taille = 1,75)

Dans cet exemple, vous mettez à jour l'âge de Jane après son anniversaire. Bien que le nom de ._remplacer() pourrait suggérer que la méthode modifie le tuple nommé existant, ce n'est pas ce qui se passe dans la pratique. Ceci est dû au fait namedtuple les instances sont immuables, donc ._remplacer() ne met pas à jour Jeanne en place.

Explorer supplémentaire namedtuple Les attributs

Les tuples nommés ont également deux attributs supplémentaires: ._des champs et ._field_defaults. Le premier attribut contient un tuple de chaînes répertoriant les noms de champ. Le deuxième attribut contient un dictionnaire qui mappe les noms de champ à leurs valeurs par défaut respectives, le cas échéant.

Dans le cas de ._des champs, vous pouvez l'utiliser pour introspecter votre namedtuple classes et instances. Vous pouvez également créer de nouvelles classes à partir de classes existantes:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge hauteur")

>>> ExtendedPerson = namedtuple(
...     "ExtendedPerson",
...     [[[[*Personne._des champs, "poids"]
... )

>>> Jeanne = ExtendedPerson("Jeanne", 26, 1,75, 67)
>>> Jeanne
ExtendedPerson (nom = 'Jane', âge = 26, taille = 1,75, poids = 67)
>>> Jeanne.poids
67

Dans cet exemple, vous créez un nouveau namedtuple appelé ExtendedPerson avec un nouveau domaine, poids. Ce nouveau type étend votre ancien Personne. Pour ce faire, vous accédez ._des champs au Personne et décompressez-le dans une nouvelle liste avec un champ supplémentaire, poids.

Vous pouvez aussi utiliser ._des champs pour parcourir les champs et les valeurs dans un namedtuple instance utilisant Python Zip *: français():

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge taille poids")
>>> Jeanne = Personne("Jeanne", 26, 1,75, 67)
>>> pour domaine, valeur dans Zip *: français(Jeanne._des champs, Jeanne):
...     imprimer(domaine, "->", valeur)
...
nom -> Jane
âge -> 26
hauteur -> 1,75
poids -> 67

Dans cet exemple, Zip *: français() donne des tuples de la forme (champ, valeur). De cette façon, vous pouvez accéder aux deux éléments de la paire champ-valeur dans le tuple nommé sous-jacent. Une autre façon d'itérer sur les champs et les valeurs en même temps pourrait être d'utiliser ._asdict (). éléments (). Vas-y, essaies!

Avec ._field_defaults, vous pouvez introspecter namedtuple classes et instances pour savoir quels champs fournissent des valeurs par défaut. Avoir des valeurs par défaut rend vos champs facultatifs. Par exemple, dites votre Personne la classe doit inclure un champ supplémentaire pour contenir le pays dans lequel la personne vit. Étant donné que vous travaillez principalement avec des personnes du Canada, vous définissez la valeur par défaut appropriée pour le pays champ comme celui-ci:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple(
...     "Personne",
...     "nom âge taille poids pays",
...     valeurs par défaut=[[[["Canada"]
... )

>>> Personne._field_defaults
'country': 'Canada'

Avec une requête rapide pour ._field_defaults, vous pouvez déterminer quels champs dans un namedtuple fournir des valeurs par défaut. Dans cet exemple, tout autre programmeur de votre équipe peut voir que votre Personne classe fournit "Canada" comme valeur par défaut pratique pour pays.

Si ton namedtuple ne fournit pas de valeurs par défaut, alors .field_defaults contient un dictionnaire vide:

>>>

>>> de collections importer namedtuple

>>> Personne = namedtuple("Personne", "nom âge taille poids pays")
>>> Personne._field_defaults

Si vous ne fournissez pas de liste de valeurs par défaut à namedtuple (), puis il s'appuie sur la valeur par défaut pour valeurs par défaut, lequel est Rien. Dans ce cas, ._field_defaults contient un dictionnaire vide.

Écrire du code pythonique avec namedtuple

On peut soutenir que le cas d'utilisation fondamental des tuples nommés est de vous aider à écrire plus de code pythonique. le namedtuple () La fonction factory a été créée pour vous permettre d'écrire du code lisible, explicite, propre et maintenable.

Dans cette section, vous allez écrire un tas d'exemples pratiques qui vous aideront à repérer les bonnes opportunités d'utiliser des tuples nommés au lieu de tuples normaux afin que vous puissiez rendre votre code plus pythonique.

Utiliser des noms de champs au lieu d'indices

Supposons que vous créez une application de peinture et que vous deviez définir les propriétés du stylet à utiliser en fonction du choix de l’utilisateur. Vous avez codé les propriétés du stylet dans un tuple:

>>>

>>> stylo = (2, "Solide", Vrai)

>>> si stylo[[[[0] == 2 et stylo[[[[1] == "Solide" et stylo[[[[2]:
...     imprimer("Stylet standard sélectionné")
...
Stylo standard sélectionné

Cette ligne de code définit un tuple avec trois valeurs. Pouvez-vous dire quelle est la signification de chaque valeur? Vous pouvez peut-être deviner que la deuxième valeur est liée au style de ligne, mais quelle est la signification de 2 et Vrai?

Vous pouvez ajouter un joli commentaire pour fournir un contexte pour stylo, auquel cas vous vous retrouveriez avec quelque chose comme ceci:

>>>

>>> # Tuple contenant: épaisseur de ligne, style de ligne et bords biseautés
>>> stylo = (2, "Solide", Vrai)

Frais! Vous connaissez maintenant la signification de chaque valeur du tuple. Cependant, que se passe-t-il si vous ou un autre programmeur utilisez stylo loin de cette définition? Ils devraient revenir à la définition juste pour se souvenir de la signification de chaque valeur.

Voici une autre mise en œuvre de stylo utilisant un namedtuple:

>>>

>>> de collections importer namedtuple

>>> Stylo = namedtuple("Stylo", "style de largeur biseauté")
>>> stylo = Stylo(2, "Solide", Vrai)

>>> si stylo.largeur == 2 et stylo.style == "Solide" et stylo.biseauté:
...     imprimer("Stylet standard sélectionné")
...
Stylo standard sélectionné

Maintenant, votre code indique clairement que 2 représente la largeur du stylo, "Solide" est le style de ligne, et ainsi de suite. Quiconque lit votre code peut le voir et le comprendre. Votre nouvelle implémentation de stylo a deux lignes de code supplémentaires. C'est une petite quantité de travail qui produit une grande victoire en termes de lisibilité et de maintenabilité.

Renvoi de plusieurs valeurs nommées à partir de fonctions

Une autre situation dans laquelle vous pouvez utiliser un tuple nommé est lorsque vous devez renvoyer plusieurs valeurs à partir d'une fonction donnée. Dans ce cas, l'utilisation d'un tuple nommé peut rendre votre code plus lisible, car les valeurs renvoyées fourniront également du contexte pour leur contenu.

Par exemple, Python fournit une fonction intégrée appelée divmod () qui prend deux nombres comme arguments et renvoie un tuple avec le quotient et reste qui résultent de la division entière des nombres d'entrée:

>>>

>>> divmod(8, 4)
(2, 0)

Pour vous souvenir de la signification de chaque nombre, vous devrez peut-être lire la documentation de divmod () parce que les nombres eux-mêmes ne fournissent pas beaucoup d’informations sur leur signification individuelle. Le nom de la fonction n’aide pas beaucoup non plus.

Voici une fonction qui utilise un namedtuple pour clarifier la signification de chaque nombre qui divmod () Retour:

>>>

>>> de collections importer namedtuple

>>> def custom_divmod(une, b):
...     DivMod = namedtuple("DivMod", "reste du quotient")
...     revenir DivMod(*divmod(une, b))
...

>>> custom_divmod(8, 4)
DivMod (quotient = 2, reste = 0)

Dans cet exemple, vous ajoutez du contexte à chaque valeur renvoyée, de sorte que tout programmeur lisant votre code peut immédiatement comprendre ce que signifie chaque nombre.

Réduction du nombre d'arguments aux fonctions

La réduction du nombre d'arguments qu'une fonction peut prendre est considérée comme une meilleure pratique de programmation. Cela rend la signature de votre fonction plus concise et optimise votre processus de test en raison du nombre réduit d'arguments et des combinaisons possibles entre eux.

Encore une fois, vous devriez envisager d'utiliser des tuples nommés pour aborder ce cas d'utilisation. Supposons que vous codiez une application pour gérer les informations de vos clients. L'application utilise une base de données pour stocker les données des clients. Pour traiter les données et mettre à jour la base de données, vous avez créé plusieurs fonctions. L'une de vos fonctions de haut niveau est Créer un utilisateur(), qui ressemble à ceci:

def Créer un utilisateur(db, Nom d'utilisateur, Nom du client, plan):
    db.add_user(Nom d'utilisateur)
    db.complete_user_profile(Nom d'utilisateur, Nom du client, plan)

Cette fonction prend quatre arguments. Le premier argument, db, représente la base de données avec laquelle vous travaillez. The rest of the arguments are closely related to a given client. This is a great opportunity to reduce the number of arguments to create_user() using a named tuple:

User = namedtuple("User", "username client_name plan")
user = User("john", "John Doe", "Premium")

def create_user(db, user):
    db.add_user(user.username)
    db.complete_user_profile(
        user.username,
        user.client_name,
        user.plan
    )

Now create_user() takes only two arguments: db et user. Inside the function, you use convenient and descriptive field names to provide the arguments to db.add_user() et db.complete_user_profile(). Your high-level function, create_user(), is more focused on the user. It’s also easier to test because you just need to provide two arguments to each test.

Reading Tabular Data From Files and Databases

A quite common use case for named tuples is to use them to store database records. You can define namedtuple classes using the column names as field names and retrieve the data from the rows in the database to named tuples. You can also do something similar with CSV files.

For example, say you have a CSV file with data regarding the employees of your company, and you want to read that data into a suitable data structure for further processing. Your CSV file looks like this:

name,job,email
"Linda","Technical Lead","linda@example.com"
"Joe","Senior Web Developer","joe@example.com"
"Lara","Project Manager","lara@example.com"
"David","Data Analyst","david@example.com"
"Jane","Senior Python Developer","jane@example.com"

You’re thinking of using Python’s csv module and its DictReader to process the file, but you have an additional requirement—you need to store the data into an immutable and lightweight data structure. In this case, a namedtuple might be a good choice:

>>>

>>> import csv
>>> from collections import namedtuple

>>> avec open("employees.csv", "r") comme csv_file:
...     reader = csv.reader(csv_file)
...     Employee = namedtuple("Employee", next(reader), rename=True)
...     pour row in reader:
...         employee = Employee(*row)
...         imprimer(employee.Nom, employee.job, employee.e-mail)
...
Linda Technical Lead linda@example.com
Joe Senior Web Developer joe@example.com
Lara Project Manager lara@example.com
David Data Analyst david@example.com
Jane Senior Python Developer jane@example.com

In this example, you first open the employees.csv file in a avec statement. Then you use csv.reader() to get an iterator over the lines in the CSV file. Avec namedtuple(), you create a new Employee class. The call to next() retrieves the first row of data from reader, which contains the CSV file header. This header provides the field names for your namedtuple.

Finally, the pour loop creates an Employee instance from each row in the CSV file and prints the list of employees to the screen.

Using namedtuple vs Other Data Structures

So far, you’ve learned how to create named tuples to make your code more readable, explicit, and Pythonic. You’ve also written some examples that help you spot opportunities for using named tuples in your code.

In this section, you’ll take a general look at the similarities and differences between namedtuple classes and other Python data structures, such as dictionaries, data classes, and typed named tuples. You’ll compare named tuples with other data structures regarding the following characteristics:

  • Readability
  • Mutability
  • Memory usage
  • Performance

This way, you’ll be better prepared to choose the right data structure for your specific use case.

namedtuple vs Dictionary

The dictionary is a fundamental data structure in Python. The language itself is built around dictionaries, so they’re everywhere. Since they’re so common and useful, you probably use them a lot in your code. But how different are dictionaries and named tuples?

In terms of readability, you can probably say that dictionaries are as readable as named tuples. Even though they don’t provide a way to access attributes through the dot notation, the dictionary-style key lookup is quite readable and straightforward:

>>>

>>> from collections import namedtuple

>>> jane = "name": "Jane", "age": 25, "height": 1.75
>>> jane[[[["age"]
25

>>> # Equivalent named tuple
>>> Person = namedtuple("Person", "name age height")
>>> jane = Person("Jane", 25, 1.75)
>>> jane.age
25

In both examples, you have a total understanding of the code and its intention. The named tuple definition requires two additional lines of code, though: one line to import the namedtuple() factory function and another to define your namedtuple class, Person.

A big difference between both data structures is that dictionaries are mutable and named tuples are immutable. This means that you can modify dictionaries in place, but you can’t modify named tuples:

>>>

>>> from collections import namedtuple

>>> jane = "name": "Jane", "age": 25, "height": 1.75
>>> jane[[[["age"] = 26
>>> jane[[[["age"]
26
>>> jane[[[["weight"] = 67
>>> jane
'name': 'Jane', 'age': 26, 'height': 1.75, 'weight': 67

>>> # Equivalent named tuple
>>> Person = namedtuple("Person", "name age height")
>>> jane = Person("Jane", 25, 1.75)

>>> jane.age = 26
Traceback (most recent call last):
  Déposer "", line 1, in 
AttributeError: can't set attribute

>>> jane.weight = 67
Traceback (most recent call last):
  Déposer "", line 1, in 
AttributeError: 'Person' object has no attribute 'weight'

You can update the value of an existing key in a dictionary, but you can’t do something similar in a named tuple. You can add new key-value pairs to existing dictionaries, but you can’t add field-value pairs to existing named tuples.

In general, if you need an immutable data structure to properly solve a given problem, then consider using a named tuple instead of a dictionary so you can meet your requirements.

Regarding memory usage, named tuples are a quite lightweight data structure. Fire up your code editor or IDE and create the following script:

# namedtuple_dict_memory.py

from collections import namedtuple
from pympler import asizeof

Point = namedtuple("Point", "x y z")
point = Point(1, 2, 3)

namedtuple_size = asizeof.asizeof(point)
dict_size = asizeof.asizeof(point._asdict())
gain = 100 - namedtuple_size / dict_size * 100

imprimer(f"namedtuple: namedtuple_size    bytes (gain:.2f% smaller)")
imprimer(f"dict:       dict_size    bytes")

This small script uses asizeof.asizeof() from Pympler to get the memory footprint of a named tuple and its equivalent dictionary.

If you run the script from your command line, then you’ll get the following output:

$ python namedtuple_dict_memory.py
namedtuple: 160 bytes (67.74% smaller)
dict:       496 bytes

This output confirms that named tuples consume less memory than equivalent dictionaries. So if memory consumption is a restriction for you, then you should consider using a named tuple instead of a dictionary.

Finally, you need to have an idea of how different named tuples and dictionaries are in terms of operations performance. To do that, you’ll test membership and attribute access operations. Get back to your code editor and create the following script:

# namedtuple_dict_time.py

from collections import namedtuple
from time import perf_counter

def average_time(structure, test_func):
    time_measurements = []
    pour _ in range(1_000_000):
        start = perf_counter()
        test_func(structure)
        end = perf_counter()
        time_measurements.append(end - start)
    return sum(time_measurements) / len(time_measurements) * int(1e9)

def time_dict(dictionary):
    "x" in dictionary
    "missing_key" in dictionary
    2 in dictionary.values()
    "missing_value" in dictionary.values()
    dictionary[[[["y"]

def time_namedtuple(named_tuple):
    "x" in named_tuple._fields
    "missing_field" in named_tuple._fields
    2 in named_tuple
    "missing_value" in named_tuple
    named_tuple.y

Point = namedtuple("Point", "x y z")
point = Point(X=1, y=2, z=3)

namedtuple_time = average_time(point, time_namedtuple)
dict_time = average_time(point._asdict(), time_dict)
gain = dict_time / namedtuple_time

imprimer(f"namedtuple: namedtuple_time:.2f    ns (gain:.2fx faster)")
imprimer(f"dict:       dict_time:.2f    ns")

This script times operations common to both dictionaries and named tuples, such as membership tests and attribute access. Running the script on your current system displays an output similar to the following:

$ namedtuple_dict_time.py
namedtuple: 527.26 ns (1.36x faster)
dict:       717.71 ns

This output shows that operations on named tuples are slightly faster than similar operations on dictionaries.

namedtuple vs Data Class

Python 3.7 came with a new cool feature: data classes. According to PEP 557, data classes are similar to named tuples, but they’re mutable:

Data Classes can be thought of as “mutable namedtuples with defaults.” (Source)

However, it’d be more accurate to say that data classes are like mutable named tuples with type hints. The “defaults” part isn’t a difference at all because named tuples can also have default values for their fields. So, at first glance, the main differences are mutability and type hints.

To create a data class, you need to import the dataclass() decorator from dataclasses. Then you can define your data classes using the regular class definition syntax:

>>>

>>> from dataclasses import dataclass

>>> @dataclass
... class Person:
...     Nom: str
...     age: int
...     height: float
...     weight: float
...     country: str = "Canada"
...

>>> jane = Person("Jane", 25, 1.75, 67)
>>> jane
Person(name='Jane', age=25, height=1.75, weight=67, country='Canada')
>>> jane.Nom
'Jane'
>>> jane.Nom = "Jane Doe"
>>> jane.Nom
'Jane Doe'

In terms of readability, there are no significant differences between data classes and named tuples. They provide similar string representations, and you can access their attributes using the dot notation.

Mutability-wise, data classes are mutable by definition, so you can change the value of their attributes when needed. However, they have an ace up their sleeve. You can set the dataclass() decorator’s frozen argument to True and make them immutable:

>>>

>>> from dataclasses import dataclass

>>> @dataclass(frozen=True)
... class Person:
...     Nom: str
...     age: int
...     height: float
...     weight: float
...     country: str = "Canada"
...

>>> jane = Person("Jane", 25, 1.75, 67)
>>> jane.Nom = "Jane Doe"
Traceback (most recent call last):
  Déposer "", line 1, in 
  
  
  
  Déposer "", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'name'

If you set frozen à True in the call to dataclass(), then you make the data class immutable. In this case, when you try to update Jane’s name, you get a FrozenInstanceError.

Another subtle difference between named tuples and data classes is that the latter aren’t iterable by default. Stick to the Jane example and try to iterate over her data:

>>>

>>> pour field in jane:
...     imprimer(field)
...
Traceback (most recent call last):
  Déposer "", line 1, in 
TypeError: 'Person' object is not iterable

If you try to iterate over a bare-bones data class, then you get a TypeError. This is common to regular classes. Fortunately, there are ways to work around it. For example, you can add an .__iter__() special method to Person like this:

>>>

>>> from dataclasses import astuple, dataclass

>>> @dataclass
... class Person:
...     Nom: str
...     age: int
...     height: float
...     weight: float
...     country: str = "Canada"
...     def __iter__(self):
...         return iter(astuple(self))
...

>>> pour field in Person("Jane", 25, 1.75, 67):
...     imprimer(field)
...
Jane
25
1.75
67
Canada

Here, you first import astuple() from dataclasses. This function converts the data class into a tuple. Then you pass the resulting tuple to iter() so you can build and return an iterator from .__iter__(). With this addition, you can start iterating over Jane’s data.

Regarding memory consumption, named tuples are more lightweight than data classes. You can confirm this by creating and running a small script similar to the one you saw in the above section. To view the complete script, expand the box below.

Here’s a script that compares memory usage between a namedtuple and its equivalent data class:

# namedtuple_dataclass_memory.py

from collections import namedtuple
from dataclasses import dataclass

from pympler import asizeof

PointNamedTuple = namedtuple("PointNamedTuple", "x y z")

@dataclass
class PointDataClass:
    X: int
    y: int
    z: int

namedtuple_memory = asizeof.asizeof(PointNamedTuple(X=1, y=2, z=3))
dataclass_memory = asizeof.asizeof(PointDataClass(X=1, y=2, z=3))
gain = 100 - namedtuple_memory / dataclass_memory * 100

imprimer(f"namedtuple: namedtuple_memory    bytes (gain:.2f% smaller)")
imprimer(f"data class: dataclass_memory    bytes")

In this script, you create a named tuple and a data class containing similar data. Then you compare their memory footprint.

Here are the results of running your script:

$ python namedtuple_dataclass_memory.py
namedtuple: 160 bytes (61.54% smaller)
data class: 416 bytes

Unlike namedtuple classes, data classes keep a per-instance .__dict__ to store writable instance attributes. This contributes to a bigger memory footprint.

Next, you can expand the section below to see an example of code that compares namedtuple classes and data classes in terms of their performance on attribute access.

The following script compares the performance of attribute access on a named tuple and its equivalent data class:

# namedtuple_dataclass_time.py

from collections import namedtuple
from dataclasses import dataclass
from time import perf_counter

def average_time(structure, test_func):
    time_measurements = []
    pour _ in range(1_000_000):
        start = perf_counter()
        test_func(structure)
        end = perf_counter()
        time_measurements.append(end - start)
    return sum(time_measurements) / len(time_measurements) * int(1e9)

def time_structure(structure):
    structure.X
    structure.y
    structure.z

PointNamedTuple = namedtuple("PointNamedTuple", "x y z", defaults=[[[[3])

@dataclass
class PointDataClass:
    X: int
    y: int
    z: int

namedtuple_time = average_time(PointNamedTuple(X=1, y=2, z=3), time_structure)
dataclass_time = average_time(PointDataClass(X=1, y=2, z=3), time_structure)
gain = dataclass_time / namedtuple_time

imprimer(f"namedtuple: namedtuple_time:.2f    ns (gain:.2fx faster)")
imprimer(f"data class: dataclass_time:.2f    ns")

Here, you time the attribute access operation because that’s almost the only common operation between a named tuple and a data class. You can also time membership operations, but you would have to access the data class’ .__dict__ attribute to do it.

In terms of performance, here are the results:

$ python namedtuple_dataclass_time.py
namedtuple: 274.32 ns (1.08x faster)
data class: 295.37 ns

The performance difference is minimal, so you can say that both data structures have equivalent performance when it comes to attribute access operations.

namedtuple vs typing.NamedTuple

Python 3.5 introduced a provisional module called typing to support function type annotations or type hints. This module provides NamedTuple, which is a typed version of namedtuple. Avec NamedTuple, you can create namedtuple classes with type hints. Following with the Person example, you can create an equivalent typed named tuple like this:

>>>

>>> from typing import NamedTuple

>>> class Person(NamedTuple):
...     Nom: str
...     age: int
...     height: float
...     weight: float
...     country: str = "Canada"
...

>>> issubclass(Person, tuple)
True
>>> jane = Person("Jane", 25, 1.75, 67)
>>> jane.Nom
'Jane'
>>> jane.Nom = "Jane Doe"
Traceback (most recent call last):
  Déposer "", line 1, in 
AttributeError: can't set attribute

Avec NamedTuple, you can create tuple subclasses that support type hints and attribute access through the dot notation. Since the resulting class is a tuple subclass, it’s immutable as well.

A subtle detail to notice in the above example is that NamedTuple subclasses look even more similar to data classes than named tuples.

When it comes to memory consumption, both namedtuple et NamedTuple instances use the same amount of memory. You can expand the box below to view a script that compares memory usage between the two.

Here’s a script that compares the memory usage of a namedtuple and its equivalent typing.NamedTuple:

# typed_namedtuple_memory.py

from collections import namedtuple
from typing import NamedTuple

from pympler import asizeof

PointNamedTuple = namedtuple("PointNamedTuple", "x y z")

class PointTypedNamedTuple(NamedTuple):
    X: int
    y: int
    z: int

namedtuple_memory = asizeof.asizeof(PointNamedTuple(X=1, y=2, z=3))
typed_namedtuple_memory = asizeof.asizeof(
    PointTypedNamedTuple(X=1, y=2, z=3)
)

imprimer(f"namedtuple:        namedtuple_memory    bytes")
imprimer(f"typing.NamedTuple: typed_namedtuple_memory    bytes")

In this script, you create a named tuple and an equivalent typed NamedTuple instance. Then you compare the memory usage of both instances.

This time, the script that compares memory usage produces the following output:

$ python typed_namedtuple_memory.py
namedtuple:        160 bytes
typing.NamedTuple: 160 bytes

In this case, both instances consume the same amount of memory, so there’s no winner this time.

Depuis namedtuple classes and NamedTuple subclasses are both subclasses of tuple, they have a lot in common. In this case, you can time membership tests for fields and values. You can also time attribute access with the dot notation. Expand the box below to view a script that compares the performance of both namedtuple et NamedTuple.

The following script compares namedtuple et typing.NamedTuple performance-wise:

# typed_namedtuple_time.py

from collections import namedtuple
from time import perf_counter
from typing import NamedTuple

def average_time(structure, test_func):
    time_measurements = []
    pour _ in range(1_000_000):
        start = perf_counter()
        test_func(structure)
        end = perf_counter()
        time_measurements.append(end - start)
    return sum(time_measurements) / len(time_measurements) * int(1e9)

def time_structure(structure):
    "x" in structure._fields
    "missing_field" in structure._fields
    2 in structure
    "missing_value" in structure
    structure.y

PointNamedTuple = namedtuple("PointNamedTuple", "x y z")

class PointTypedNamedTuple(NamedTuple):
    X: int
    y: int
    z: int

namedtuple_time = average_time(PointNamedTuple(X=1, y=2, z=3), time_structure)
typed_namedtuple_time = average_time(
    PointTypedNamedTuple(X=1, y=2, z=3), time_structure
)

imprimer(f"namedtuple:        namedtuple_time:.2f    ns")
imprimer(f"typing.NamedTuple: typed_namedtuple_time:.2f    ns")

In this script, you first create a named tuple and then a typed named tuple with similar content. Then you compare the performance of common operations over both data structures.

Here are the results:

$ python typed_namedtuple_time.py
namedtuple:        503.34 ns
typing.NamedTuple: 509.91 ns

In this case, you can say that both data structures behave almost the same in terms of performance. Other than that, using NamedTuple to create your named tuples can make your code even more explicit because you can add type information to the fields. You can also provide default values, add new functionality, and write docstrings for your typed named tuples.

In this section, you’ve learned a lot about namedtuple and other similar data structures and classes. Here’s a table that summarizes how namedtuple compares to the data structures covered in this section:

dict Data Class NamedTuple
Readability Similar Equal Equal
Immutability Non No by default, yes if using @dataclass(frozen=True) Yes
Memory usage Higher Higher Equal
Performance Slower Similar Similar
Iterability Yes No by default, yes if providing .__iter__() Yes

With this summary, you’ll be able to choose the data structure that best fits your current needs. Additionally, you should consider that data classes and NamedTuple allow you to add type hints, which is currently quite a desirable feature in Python code.

Subclassing namedtuple Classes

Depuis namedtuple classes are regular Python classes, you can subclass them if you need to provide additional functionalities, a docstring, a user-friendly string representation, and so on.

For example, storing the age of a person in an object isn’t considered a best practice. So you probably want to store the birth date and compute the age when needed:

>>>

>>> from collections import namedtuple
>>> from datetime import date

>>> BasePerson = namedtuple(
...     "BasePerson",
...     "name birthdate country",
...     defaults=[[[["Canada"]
... )

>>> class Person(BasePerson):
...     """A namedtuple subclass to hold a person's data."""
...     __slots__ = ()
...     def __repr__(self):
...         return f"Name: self.Nom, age: self.age    years old."
...     @property
...     def age(self):
...         return (date.aujourd'hui() - self.birthdate).journées // 365
...

>>> Person.__doc__
"A namedtuple subclass to hold a person's data."

>>> jane = Person("Jane", date(1996, 3, 5))
>>> jane.age
25
>>> jane
Name: Jane, age: 25 years old.

Person inherits from BasePerson, which is a namedtuple class. In the subclass definition, you first add a docstring to describe what the class does. Then you set __slots__ to an empty tuple, which prevents the automatic creation of a per-instance .__dict__. This keeps your BasePerson subclass memory efficient.

You also add a custom .__repr__() to provide a nice string representation for the class. Finally, you add a property to compute the person’s age using datetime.

Measuring Creation Time: tuple vs namedtuple

So far, you’ve compared namedtuple classes with other data structures according to several features. In this section, you’ll take a general look at how regular tuples and named tuples compare in terms of creation time.

Say you have an application that creates a ton of tuples dynamically. You decide to make your code more Pythonic and maintainable using named tuples. Once you’ve updated all your codebase to use named tuples, you run the application and notice some performance issues. After some tests, you conclude that the issues could be related to creating named tuples dynamically.

Here’s a script that measures the average time required to create several tuples and named tuples dynamically:

# tuple_namedtuple_time.py

from collections import namedtuple
from time import perf_counter

def average_time(test_func):
    time_measurements = []
    pour _ in range(1_000):
        start = perf_counter()
        test_func()
        end = perf_counter()
        time_measurements.append(end - start)
    return sum(time_measurements) / len(time_measurements) * int(1e9)

def time_tuple():
    tuple([[[[1] * 1000)

fields = [[[[f"an" pour n in range(1000)]
TestNamedTuple = namedtuple("TestNamedTuple", fields)

def time_namedtuple():
    TestNamedTuple(*([[[[1] * 1000))

namedtuple_time = average_time(time_namedtuple)
tuple_time = average_time(time_tuple)
gain = namedtuple_time / tuple_time

imprimer(f"tuple:      tuple_time:.2f    ns (gain:.2fx faster)")
imprimer(f"namedtuple: namedtuple_time:.2f    ns")

In this script, you calculate the average time it takes to create several tuples and their equivalent named tuples. If you run the script from your command line, then you’ll get an output similar to the following:

$ python tuple_namedtuple_time.py
tuple:      7075.82 ns (3.36x faster)
namedtuple: 23773.67 ns

When you look at this output, you can see that creating tuple objects dynamically is a lot faster than creating similar named tuples. In some situations, such as working with large databases, the additional time required to create a named tuple can seriously affect your application’s performance, so keep an eye on this if your code creates a lot of tuples dynamically.

Conclusion

Writing Pythonic code is an in-demand skill in the Python development space. Pythonic code is readable, explicit, clean, maintainable, and takes advantage of Python idioms and best practices. In this tutorial, you learned about creating namedtuple classes and instances and how they can help you improve the quality of your Python code.

In this tutorial, you learned:

  • How to create and use namedtuple classes and instances
  • How to take advantage of cool namedtuple features
  • When to use namedtuple instances to write Pythonic code
  • When to use a namedtuple instead of a similar data structure
  • How to subclass a namedtuple to add new features

With this knowledge, you can deeply improve the quality of your existing and future code. If you frequently use tuples, then consider turning them into named tuples whenever it makes sense. Doing so will make your code much more readable and Pythonic.

[ad_2]