Expert Python
Un problème courant que vous pouvez rencontrer lorsque vous travaillez avec des dictionnaires Python est d'essayer d'accéder ou de modifier des clés qui n'existent pas dans le dictionnaire. Cela soulèvera un KeyError et briser l'exécution de votre code. Pour gérer ce genre de situations, la bibliothèque standard fournit le Python defaultdict type, une classe de type dictionnaire qui est disponible pour vous dans collections.
Le Python defaultdict se comporte presque exactement comme un dictionnaire Python standard, mais si vous essayez d'accéder ou de modifier une clé manquante, defaultdict crée automatiquement la clé et génère une valeur par défaut pour celle-ci. Cela fait defaultdict une option précieuse pour gérer les clés manquantes dans les dictionnaires.
Dans ce didacticiel, vous apprendrez:
- Comment utiliser le Python
defaultdicttaper pour gestion des clés manquantes dans un dictionnaire - Quand et pourquoi utiliser un Python
defaultdictplutôt qu'un habituédicter - Comment utiliser un
defaultdictpour regroupement, compte, et accumuler opérations
Avec ces connaissances à votre disposition, vous serez en meilleure condition pour utiliser efficacement le Python defaultdict tapez vos défis de programmation au jour le jour.
Pour tirer le meilleur parti de ce didacticiel, vous devez avoir une compréhension préalable de ce que sont les dictionnaires Python et comment les utiliser. Si vous avez besoin de vous rafraîchir, consultez les ressources suivantes:
Gestion des clés manquantes dans les dictionnaires
Un problème courant que vous pouvez rencontrer lorsque vous travaillez avec des dictionnaires Python est comment gérer les clés manquantes. Si votre code est fortement basé sur des dictionnaires, ou si vous créez des dictionnaires à la volée tout le temps, vous remarquerez bientôt que le traitement des KeyError les exceptions peuvent être assez ennuyeuses et peuvent ajouter une complexité supplémentaire à votre code. Avec les dictionnaires Python, vous avez au moins quatre façons disponibles de gérer les clés manquantes:
- Utilisation
.définir par defaut() - Utilisation
.avoir() - Utilisez le
entrer le dictidiome - Utiliser un
essayeretsaufbloquer
Les documents Python expliquent .définir par defaut() et .avoir() comme suit:
setdefault (clé[, default])Si
cléest dans le dictionnaire, retourne sa valeur. Sinon, insérezcléavec une valeur dedéfautet retourdéfaut.défautpar défaut àAucun.
Obtenir la clé[, default])Renvoie la valeur de
clésicléest dans le dictionnaire, sinondéfaut. Sidéfautn'est pas donné, il est par défautAucun, de sorte que cette méthode ne soulève jamaisKeyError.(La source)
Voici un exemple d'utilisation .définir par defaut() gérer les clés manquantes dans un dictionnaire:
>>> a_dict =
>>> a_dict[[[['missing_key']
Traceback (dernier appel le plus récent):
Fichier "" , ligne 1, dans
a_dict[[[['missing_key']
KeyError: 'missing_key'
>>> a_dict.définir par defaut('missing_key', 'valeur par défaut')
'valeur par défaut'
>>> a_dict[[[['missing_key']
'valeur par défaut'
>>> a_dict.définir par defaut('missing_key', «une autre valeur par défaut»)
'valeur par défaut'
>>> a_dict
'missing_key': 'valeur par défaut'
Dans le code ci-dessus, vous utilisez .définir par defaut() pour générer une valeur par défaut pour clé_ manquante. Notez que votre dictionnaire, a_dict, a maintenant une nouvelle clé appelée clé_ manquante dont la valeur est 'valeur par défaut'. Cette clé n'existait pas avant que vous ayez appelé .définir par defaut(). Enfin, si vous appelez .définir par defaut() sur une clé existante, l'appel n'aura aucun effet sur le dictionnaire. Votre clé contiendra la valeur d'origine au lieu de la nouvelle valeur par défaut.
Remarque: Dans l'exemple de code ci-dessus, vous obtenez une exception et Python vous montre un traceback message, qui vous indique que vous essayez d'accéder à une clé manquante dans a_dict. Si vous souhaitez approfondir la façon de déchiffrer et de comprendre un traçage Python, consultez la section Comprendre le traçage Python.
En revanche, si vous utilisez .avoir(), vous pouvez alors coder quelque chose comme ceci:
>>> a_dict =
>>> a_dict.avoir('missing_key', 'valeur par défaut')
'valeur par défaut'
>>> a_dict
Ici, vous utilisez .avoir() pour générer une valeur par défaut pour clé_ manquante, mais cette fois, votre dictionnaire reste vide. Ceci est dû au fait .avoir() renvoie la valeur par défaut, mais cette valeur n'est pas ajoutée au dictionnaire sous-jacent. Par exemple, si vous avez un dictionnaire appelé ré, alors vous pouvez supposer que .avoir() fonctionne quelque chose comme ceci:
D.get (clé, par défaut) -> D[key] si clé en D, sinon par défaut
Avec ce pseudo-code, vous pouvez comprendre comment .avoir() travaille en interne. Si la clé existe, alors .avoir() renvoie la valeur mappée à cette clé. Sinon, la valeur par défaut est retournée. Votre code ne crée ni n'attribue jamais de valeur à clé. Dans cet exemple, défaut par défaut à Aucun.
Vous pouvez également utiliser des instructions conditionnelles pour gérer les clés manquantes dans les dictionnaires. Jetez un œil à l'exemple suivant, qui utilise le entrer le dict idiome:
>>> a_dict =
>>> si 'clé' dans a_dict:
... # Faites quelque chose avec 'clé' ...
... a_dict[[[['clé']
... autre:
... a_dict[[[['clé'] = 'valeur par défaut'
...
>>> a_dict
'clé': 'valeur par défaut'
Dans ce code, vous utilisez un si déclaration avec le dans opérateur pour vérifier si clé est présent dans a_dict. Si c'est le cas, vous pouvez effectuer n'importe quelle action avec clé ou avec sa valeur. Sinon, vous créez la nouvelle clé, cléet lui attribuer un 'valeur par défaut'. Notez que le code ci-dessus fonctionne de manière similaire à .définir par defaut() mais prend quatre lignes de code, tandis que .définir par defaut() ne prendrait qu'une seule ligne (en plus d'être plus lisible).
Vous pouvez également vous promener KeyError en utilisant un essayer et sauf bloc pour gérer l'exception. Considérez le morceau de code suivant:
>>> a_dict =
>>> essayer:
... # Faites quelque chose avec 'clé' ...
... a_dict[[[['clé']
... sauf KeyError:
... a_dict[[[['clé'] = 'valeur par défaut'
...
>>> a_dict
'clé': 'valeur par défaut'
le essayer et sauf bloc dans l'exemple ci-dessus attrape le KeyError chaque fois que vous essayez d'accéder à une clé manquante. dans le sauf clause, vous créez le clé et lui attribuer un 'valeur par défaut'.
Remarque: Si les clés manquantes sont rares dans votre code, vous préférerez peut-être utiliser un essayer et sauf bloc (style de codage EAFP) pour attraper le KeyError exception. En effet, le code ne vérifie pas l’existence de chaque clé et ne gère que quelques exceptions, le cas échéant.
En revanche, si les clés manquantes sont assez courantes dans votre code, l'instruction conditionnelle (style de codage LBYL) peut être un meilleur choix car la recherche de clés peut être moins coûteuse que la gestion d'exceptions fréquentes.
Jusqu'à présent, vous avez appris à gérer les clés manquantes à l'aide des outils dicter et Python vous offre. Cependant, les exemples que vous avez vus ici sont assez verbeux et difficiles à lire. Ils peuvent ne pas être aussi simples que vous le souhaitez. C'est pourquoi la bibliothèque standard Python fournit une solution plus élégante, Pythonique et efficace. Cette solution est collections.defaultdict, et c'est ce que vous allez couvrir désormais.
Comprendre le Python defaultdict Type
La bibliothèque standard Python fournit collections, qui est un module qui implémente des types de conteneurs spécialisés. L'un d'eux est le Python defaultdict type, qui est une alternative à dicter spécialement conçu pour vous aider avec les clés manquantes. defaultdict est un type Python qui hérite de dicter:
>>> de collections importation defaultdict
>>> issubclass(defaultdict, dicter)
Vrai
Le code ci-dessus montre que le Python defaultdict le type est un sous-classe de dicter. Cela signifie que defaultdict hérite de la plupart du comportement de dicter. Vous pouvez donc dire que defaultdict ressemble beaucoup à un dictionnaire ordinaire.
La principale différence entre defaultdict et dicter est que lorsque vous essayez d'accéder ou de modifier un clé qui n'est pas présent dans le dictionnaire, une valeur par défaut valeur est automatiquement attribué à cette clé. Afin de fournir cette fonctionnalité, le Python defaultdict type fait deux choses:
- Il remplace
.__disparu__(). - Il ajoute
.default_factory, une variable d'instance accessible en écriture qui doit être fournie au moment de l'instanciation.
La variable d'instance .default_factory contiendra le premier argument passé dans defaultdict .__ init __ (). Cet argument peut prendre un appelable Python valide ou Aucun. Si un appelable est fourni, il sera automatiquement appelé par defaultdict chaque fois que vous essayez d'accéder ou de modifier la valeur associée à une clé manquante.
Remarque: Tous les arguments restants de l'initialiseur de classe sont traités comme s'ils étaient passés à l'initialiseur de regular dicter, y compris les arguments du mot clé.
Découvrez comment créer et initialiser correctement un defaultdict:
>>> # Instanciation correcte
>>> def_dict = defaultdict(liste) # Passer la liste à .default_factory
>>> def_dict[[[['un'] = 1 # Ajouter une paire valeur / clé
>>> def_dict[[[['disparu'] # Accéder à une clé manquante renvoie une liste vide
[]
>>> def_dict[[[['another_missing'].ajouter(4) # Modifier une clé manquante
>>> def_dict
defaultdict (, 'un': 1, 'manquant': [], 'another_missing': [4])
Ici, tu passes liste à .default_factory lorsque vous créez le dictionnaire. Ensuite, vous utilisez def_dict comme un dictionnaire ordinaire. Notez que lorsque vous essayez d'accéder ou de modifier la valeur mappée à une clé inexistante, le dictionnaire lui attribue la valeur par défaut résultant de l'appel liste().
Gardez à l'esprit que vous devez transmettre un objet appelable Python valide à .default_factory, n'oubliez donc pas de l'appeler en utilisant les parenthèses au moment de l'initialisation. Cela peut être un problème courant lorsque vous commencez à utiliser Python defaultdict type. Jetez un œil au code suivant:
>>> # Mauvaise instanciation
>>> def_dict = defaultdict(liste())
Traceback (dernier appel le plus récent):
Fichier "" , ligne 1, dans
def_dict = defaultdict(liste())
Erreur-type: le premier argument doit pouvoir être appelé ou Aucun
Ici, vous essayez de créer un defaultdict en passant liste() à .default_factory. L'appel à liste() soulève un Erreur-type, qui vous indique que le premier argument doit être appelable ou Aucun.
Avec cette introduction au Python defaultdict tapez, vous pouvez commencer à coder avec des exemples pratiques. Les sections suivantes vous guideront à travers quelques cas d’utilisation courants où vous pouvez defaultdict pour fournir une solution élégante, efficace et Pythonique.
Utiliser le Python defaultdict Type
Parfois, vous utiliserez une collection intégrée mutable (un liste, dicter, ou ensemble) comme valeurs dans vos dictionnaires Python. Dans ces cas, vous devrez initialiser les clés avant la première utilisation, ou vous obtiendrez un KeyError. Vous pouvez soit faire ce processus manuellement ou l'automatiser à l'aide d'un Python defaultdict. Dans cette section, vous apprendrez à utiliser Python defaultdict type pour résoudre certains problèmes de programmation courants:
- Regroupement les éléments d'une collection
- Compte les éléments d'une collection
- Accumuler les valeurs d'une collection
Vous couvrirez quelques exemples qui utilisent liste, ensemble, int, et flotte pour effectuer des opérations de regroupement, de comptage et d'accumulation de manière conviviale et efficace.
Regroupement d'éléments
Une utilisation typique du Python defaultdict le type est à définir .default_factory à liste puis créez un dictionnaire qui mappe les clés aux listes de valeurs. Avec ça defaultdict, si vous essayez d'accéder à une clé manquante, le dictionnaire exécute les étapes suivantes:
- Appel
liste()pour créer un nouveau videliste - Insérer le vide
listedans le dictionnaire en utilisant la clé manquante commeclé - Revenir une référence à cela
liste
Cela vous permet d'écrire du code comme ceci:
>>> de collections importation defaultdict
>>> dd = defaultdict(liste)
>>> dd[[[['clé'].ajouter(1)
>>> dd
defaultdict (, 'clé': [1])
>>> dd[[[['clé'].ajouter(2)
>>> dd
defaultdict (, 'clé': [1, 2])
>>> dd[[[['clé'].ajouter(3)
>>> dd
defaultdict (, 'clé': [1, 2, 3])
Ici, vous créez un Python defaultdict appelé dd et passer liste à .default_factory. Notez que même lorsque clé n'est pas défini, vous pouvez y ajouter des valeurs sans obtenir de KeyError. C'est parce que dd appelle automatiquement .default_factory pour générer une valeur par défaut pour le manquant clé.
Vous pouvez utiliser defaultdict de même que liste pour regrouper les éléments dans une séquence ou une collection. Supposons que vous ayez récupéré les données suivantes de la base de données de votre entreprise:
| département | Nom de l'employé |
|---|---|
| Ventes | John Doe |
| Ventes | Martin Smith |
| Comptabilité | Jane Doe |
| Commercialisation | Elizabeth Smith |
| Commercialisation | Adam Doe |
| … | … |
Avec ces données, vous créez une première liste de tuple des objets comme les suivants:
dep = [([([([('Ventes', «John Doe»),
('Ventes', «Martin Smith»),
('Comptabilité', 'Jane Doe'),
('Commercialisation', «Elizabeth Smith»),
('Commercialisation', «Adam Doe»)]
Maintenant, vous devez créer un dictionnaire qui regroupe les employés par service. Pour ce faire, vous pouvez utiliser un defaultdict comme suit:
de collections importation defaultdict
dep_dd = defaultdict(liste)
pour département, employé dans dep:
dep_dd[[[[département].ajouter(employé)
Ici, vous créez un defaultdict appelé dep_dd et utiliser un pour boucle pour parcourir votre dep liste. La déclaration dep_dd[department].append (employé) crée les clés des départements, les initialise dans une liste vide, puis ajoute les employés à chaque département. Une fois que vous avez exécuté ce code, votre dep_dd ressemblera à ceci:
defaultdict (, 'Ventes': ['John Doe', 'Martin Smith'],
'Comptabilité' : ['Jane Doe'],
'Commercialisation': ['Elizabeth Smith', 'Adam Doe'])
Dans cet exemple, vous regroupez les employés par service en utilisant un defaultdict avec .default_factory mis à liste. Pour ce faire avec un dictionnaire standard, vous pouvez utiliser dict.setdefault () comme suit:
dep_d = dicter()
pour département, employé dans dep:
dep_d.définir par defaut(département, []).ajouter(employé)
Ce code est simple et vous trouverez assez souvent du code similaire dans votre travail de codeur Python. Cependant, le defaultdict La version est sans doute plus lisible, et pour les grands ensembles de données, elle peut également être beaucoup plus rapide et plus efficace. Donc, si la vitesse vous préoccupe, vous devriez envisager d'utiliser un defaultdict au lieu d'une norme dicter.
Regroupement d'articles uniques
Continuez à travailler avec les données des départements et des employés de la section précédente. Après un certain traitement, vous vous rendez compte que quelques employés ont été dupliqué dans la base de données par erreur. Vous devez nettoyer les données et supprimer les employés en double de votre dep_dd dictionnaire. Pour ce faire, vous pouvez utiliser un ensemble comme le .default_factory et réécrivez votre code comme suit:
dep = [([([([('Ventes', «John Doe»),
('Ventes', «Martin Smith»),
('Comptabilité', 'Jane Doe'),
('Commercialisation', «Elizabeth Smith»),
('Commercialisation', «Elizabeth Smith»),
('Commercialisation', «Adam Doe»),
('Commercialisation', «Adam Doe»),
('Commercialisation', «Adam Doe»)]
dep_dd = defaultdict(ensemble)
pour département, employé dans articles:
dep_dd[[[[département].ajouter(employé)
Dans cet exemple, vous définissez .default_factory à ensemble. Ensembles sont collections d'objets uniques, ce qui signifie que vous ne pouvez pas créer un ensemble avec des éléments répétés. Il s'agit d'une fonctionnalité très intéressante des ensembles, qui garantit que vous n'aurez pas d'éléments répétés dans votre dictionnaire final.
Compter les articles
Si vous définissez .default_factory à int, alors votre defaultdict sera utile pour compter les articles dans une séquence ou une collection. Quand vous appelez int () sans arguments, la fonction retourne 0, qui est la valeur typique que vous utilisez pour initialiser un compteur.
Pour continuer avec l'exemple de la base de données de l'entreprise, supposons que vous souhaitiez créer un dictionnaire qui compte le nombre d'employés par service. Dans ce cas, vous pouvez coder quelque chose comme ceci:
>>> de collections importation defaultdict
>>> dep = [([([([('Ventes', «John Doe»),
... ('Ventes', «Martin Smith»),
... ('Comptabilité', 'Jane Doe'),
... ('Commercialisation', «Elizabeth Smith»),
... ('Commercialisation', «Adam Doe»)]
>>> dd = defaultdict(int)
>>> pour département, _ dans dep:
... dd[[[[département] + = 1
>>> dd
defaultdict (, 'Ventes': 2, 'Comptabilité': 1, 'Marketing': 2)
Ici, vous définissez .default_factory à int. Quand vous appelez int () sans argument, la valeur renvoyée est 0. Vous pouvez utiliser cette valeur par défaut pour commencer à compter les employés qui travaillent dans chaque service. Pour que ce code fonctionne correctement, vous avez besoin d'un ensemble de données propre. Il ne doit pas y avoir de données répétées. Sinon, vous devrez filtrer les employés répétés.
Un autre exemple de comptage des éléments est le Mississippi exemple, où vous comptez le nombre de fois que chaque lettre d'un mot est répétée. Jetez un œil au code suivant:
>>> de collections importation defaultdict
>>> s = 'Mississippi'
>>> dd = defaultdict(int)
>>> pour lettre dans s:
... dd[[[[lettre] + = 1
...
>>> dd
defaultdict (, 'm': 1, 'i': 4, 's': 4, 'p': 2)
Dans le code ci-dessus, vous créez un defaultdict avec .default_factory mis à int. Cela définit la valeur par défaut d'une clé donnée sur 0. Ensuite, vous utilisez un pour boucle pour parcourir la chaîne s et utiliser une opération d'affectation augmentée pour ajouter 1 au comptoir à chaque itération. Les clés de dd seront les lettres Mississippi.
Remarque: Les opérateurs d'affectation augmentée de Python sont un raccourci pratique pour les opérations courantes.
Jetez un œil aux exemples suivants:
var + = 1est équivalent àvar = var + 1var - = 1est équivalent àvar = var - 1var * = 1est équivalent àvar = var * 1
Ceci n'est qu'un exemple du fonctionnement des opérateurs d'affectation augmentée. Vous pouvez consulter la documentation officielle pour en savoir plus sur cette fonctionnalité.
Comme le comptage est une tâche relativement courante en programmation, la classe de type dictionnaire Python collections.Counter est spécialement conçu pour compter les articles dans une séquence. Avec Compteur, vous pouvez écrire le Mississippi exemple comme suit:
>>> de collections importation Compteur
>>> compteur = Compteur('Mississippi')
>>> compteur
Compteur ('i': 4, 's': 4, 'p': 2, 'm': 1)
Dans ce cas, Compteur fait tout le travail pour vous! Vous n'avez qu'à passer dans une séquence, et le dictionnaire comptera ses éléments, les stockant comme clés et les comptes comme valeurs. Notez que cet exemple fonctionne car les chaînes Python sont également un type de séquence.
Accumuler des valeurs
Parfois, vous devrez calculer le somme totale des valeurs dans une séquence ou une collection. Supposons que vous ayez la feuille Excel suivante avec des données sur les ventes de votre site Web Python:
| Des produits | juillet | août | septembre |
|---|---|---|---|
| Livres | 1250,00 | 1300,00 | 1420,00 |
| Tutoriels | 560,00 | 630,00 | 750,00 |
| Cours | 2500,00 | 2430.00 | 2750,00 |
Ensuite, vous traitez les données à l'aide de Python et obtenez les éléments suivants liste de tuple objets:
revenus = [([([([('Livres', 1250,00),
('Livres', 1300,00),
('Livres', 1420,00),
('Tutoriels', 560,00),
('Tutoriels', 630,00),
('Tutoriels', 750,00),
('Cours', 2500,00),
('Cours', 2430.00),
('Cours', 2750,00),]
Avec ces données, vous souhaitez calculer le revenu total par produit. Pour ce faire, vous pouvez utiliser un Python defaultdict avec flotte comme .default_factory puis coder quelque chose comme ceci:
1 de collections importation defaultdict
2
3 dd = defaultdict(flotte)
4 pour produit, le revenu dans revenus:
5 dd[[[[produit] + = le revenu
6
sept pour produit, le revenu dans dd.articles():
8 impression(F«Revenu total pour produit: $revenus:,. 2f")
Voici ce que fait ce code:
- En ligne 1, vous importez le Python
defaultdicttype. - En ligne 3, vous créez un
defaultdictobjet avec.default_factorymis àflotte. - En ligne 4, vous définissez un
pourboucle pour parcourir les éléments derevenus. - En ligne 5, vous utilisez une opération d'affectation augmentée (
+ =) pour accumuler les revenus par produit dans le dictionnaire.
La deuxième boucle parcourt les éléments de dd et imprime les revenus sur votre écran.
Si vous mettez tout ce code dans un fichier appelé revenus.py et l'exécuter à partir de votre ligne de commande, vous obtiendrez la sortie suivante:
$ python3 revenus.py
Revenu total pour les livres: 3 970,00 $
Revenu total pour les tutoriels: 1 940,00 $
Revenu total pour les cours: 7 680,00 $
Vous avez maintenant un résumé des revenus par produit, vous pouvez donc prendre des décisions sur la stratégie à suivre pour augmenter le revenu total de votre site.
Plonger plus profondément defaultdict
Jusqu'à présent, vous avez appris à utiliser le Python defaultdict tapez en codant quelques exemples pratiques. À ce stade, vous pouvez plonger plus profondément dans implémentation de type et d'autres détails de travail. C'est ce que vous couvrirez dans les prochaines sections.
defaultdict contre dicter
Pour que vous compreniez mieux le Python defaultdict type, un bon exercice serait de le comparer avec sa superclasse, dicter. Si vous voulez connaître les méthodes et les attributs spécifiques à Python defaultdict , vous pouvez exécuter la ligne de code suivante:
>>> ensemble(dir(defaultdict)) - ensemble(dir(dicter))
'__copy__', 'default_factory', '__missing__'
Dans le code ci-dessus, vous utilisez dir () pour obtenir la liste des attributs valides pour dicter et defaultdict. Ensuite, vous utilisez un ensemble différence pour obtenir l'ensemble des méthodes et des attributs que vous ne pouvez trouver que dans defaultdict. Comme vous pouvez le voir, les différences entre ces deux classes sont. Vous avez deux méthodes et un attribut d'instance. Le tableau suivant montre à quoi servent les méthodes et l'attribut:
| Méthode ou attribut | La description |
|---|---|
.__copie__() |
Fournit un support pour copy.copy () |
.default_factory |
Contient l'appelable appelé par .__disparu__() pour fournir automatiquement des valeurs par défaut pour les clés manquantes |
.__ manquant __ (clé) |
Obtient appelé quand .__obtenir l'article__() ne trouve pas clé |
Dans le tableau ci-dessus, vous pouvez voir les méthodes et l'attribut qui font un defaultdict différent d'un habitué dicter. Les autres méthodes sont les mêmes dans les deux classes.
Remarque: Si vous initialisez un defaultdict en utilisant un callable valide, vous n'obtiendrez pas KeyError lorsque vous essayez d'accéder à une clé manquante. Toute clé qui n'existe pas obtient la valeur renvoyée par .default_factory.
En outre, vous remarquerez peut-être qu'un defaultdict est égal à dicter avec les mêmes éléments:
>>> std_dict = dicter(Nombres=[[[[1, 2, 3], des lettres=[[[['une', «b», «c»])
>>> std_dict
'Nombres': [1, 2, 3], 'des lettres': ['a', 'b', 'c']
>>> def_dict = defaultdict(liste, Nombres=[[[[1, 2, 3], des lettres=[[[['une', «b», «c»])
>>> def_dict
defaultdict (, 'Nombres': [1, 2, 3], 'des lettres': ['a', 'b', 'c'])
>>> std_dict == def_dict
Vrai
Ici, vous créez un dictionnaire régulier std_dict avec quelques éléments arbitraires. Ensuite, vous créez un defaultdict avec les mêmes éléments. Si vous testez l’égalité de contenu des deux dictionnaires, vous verrez qu’ils sont égaux.
defaultdict.default_factory
Le premier argument du Python defaultdict le type doit être un appelable qui ne prend aucun argument et renvoie une valeur. Cet argument est affecté à l'attribut d'instance, .default_factory. Pour cela, vous pouvez utiliser tout appelable, y compris les fonctions, méthodes, classes, objets de type ou tout autre appelable valide. La valeur par défaut de .default_factory est Aucun.
Si vous instanciez defaultdict sans passer une valeur à .default_factory, le dictionnaire se comportera comme un habitué dicter et l'habituel KeyError sera levée pour les tentatives de recherche ou de modification de clés manquantes:
>>> de collections importation defaultdict
>>> dd = defaultdict()
>>> dd[[[['missing_key']
Traceback (dernier appel le plus récent):
Fichier "" , ligne 1, dans
dd[[[['missing_key']
KeyError: 'missing_key'
Ici, vous instanciez le Python defaultdict tapez sans arguments. Dans ce cas, l'instance se comporte comme un dictionnaire standard. Donc, si vous essayez d'accéder ou de modifier une clé manquante, vous obtiendrez alors l'habituel KeyError. À partir de ce moment, vous pouvez utiliser dd comme un dictionnaire Python normal et, sauf si vous attribuez un nouveau callable à .default_factory, vous ne pourrez pas utiliser la capacité de defaultdict pour gérer automatiquement les clés manquantes.
Si vous passez Aucun au premier argument de defaultdict, l'instance se comportera de la même manière que vous l'avez vu dans l'exemple ci-dessus. C'est parce que .default_factory par défaut à Aucun, les deux initialisations sont donc équivalentes. D'un autre côté, si vous passez un objet appelable valide à .default_factory, vous pouvez ensuite l'utiliser pour gérer les clés manquantes de manière conviviale. Voici un exemple où vous passez liste à .default_factory:
>>> dd = defaultdict(liste, des lettres=[[[['une', «b», «c»])
>>> dd.default_factory
>>> dd
defaultdict (, 'des lettres': ['a', 'b', 'c'])
>>> dd[[[['Nombres']
[]
>>> dd
defaultdict (, 'des lettres': ['a', 'b', 'c'], 'Nombres': [])
>>> dd[[[['Nombres'].ajouter(1)
>>> dd
defaultdict (, 'des lettres': ['a', 'b', 'c'], 'Nombres': [1])
>>> dd[[[['Nombres'] + = [[[[2, 3]
>>> dd
defaultdict (, 'des lettres': ['a', 'b', 'c'], 'Nombres': [1, 2, 3])
Dans cet exemple, vous créez un Python defaultdict appelé dd, alors vous utilisez liste pour son premier argument. Le deuxième argument est appelé des lettres et détient une liste de lettres. Tu vois ça .default_factory détient maintenant un liste objet qui sera appelé lorsque vous devrez fournir une valeur par défaut valeur pour toute clé manquante.
Notez que lorsque vous essayez d'accéder à Nombres, dd teste si Nombres est dans le dictionnaire. Si ce n'est pas le cas, il appelle .default_factory (). Puisque .default_factory détient un liste objet, le retourné valeur est une liste vide ([]).
Maintenant que dd['numbers'] est initialisé avec un vide liste, vous pouvez utiliser .ajouter() pour ajouter des éléments à la liste. Vous pouvez également utiliser un opérateur d'affectation augmenté (+ =) pour concaténer les listes [1] et [2, 3]. De cette façon, vous pouvez gérer les clés manquantes de manière plus Pythonic et plus efficace.
D'un autre côté, si vous passez un non appelable objet à l'initialiseur du Python defaultdict tapez, vous obtiendrez un Erreur-type comme dans le code suivant:
>>> defaultdict(0)
Traceback (dernier appel le plus récent):
Fichier "" , ligne 1, dans
defaultdict(0)
Erreur-type: le premier argument doit pouvoir être appelé ou Aucun
Ici, tu passes 0 à .default_factory. Puisque 0 n'est pas un objet appelable, vous obtenez un Erreur-type vous dire que le premier argument doit être appelable ou Aucun. Autrement, defaultdict ne fonctionne pas.
Garde en tête que .default_factory est uniquement appelé depuis .__obtenir l'article__() et pas d'autres méthodes. Cela signifie que si dd est un defaultdict et clé est une clé manquante, alors dd[key] appellera .default_factory pour fournir une valeur par défaut valeur, mais dd.get (clé) revient toujours Aucun au lieu de la valeur .default_factory fournirait. C'est parce que .avoir() n'appelle pas .__obtenir l'article__() pour récupérer le clé.
Jetez un œil au code suivant:
>>> dd = defaultdict(liste)
>>> # Appelle jj .__ getitem __ ('manquant')
>>> dd[[[['disparu']
[]
>>> # N'appelez pas dd .__ getitem __ ('another_missing')
>>> impression(dd.avoir('another_missing'))
Aucun
>>> dd
defaultdict (, 'disparu': [])
Dans ce fragment de code, vous pouvez voir que dd.get () Retour Aucun plutôt que la valeur par défaut .default_factory fournirait. C'est parce que .default_factory est uniquement appelé depuis .__disparu__(), qui n'est pas appelé par .avoir().
Notez que vous pouvez également ajouter valeurs arbitraires à un Python defaultdict. Cela signifie que vous n'êtes pas limité aux valeurs du même type que les valeurs générées par .default_factory. Voici un exemple:
>>> dd = defaultdict(liste)
>>> dd
defaultdict (, )
>>> dd[[[['chaîne'] = «une chaîne»
>>> dd
defaultdict (, 'chaîne': 'une chaîne')
>>> dd[[[['liste']
[]
>>> dd
defaultdict (, 'chaîne': 'une chaîne', 'liste': [])
Ici, vous créez un defaultdict et passer dans un liste s'opposer à .default_factory. Cela définit vos valeurs par défaut comme des listes vides. Cependant, vous pouvez librement ajouter une nouvelle clé contenant des valeurs d'un type différent. C’est le cas avec la clé chaîne, qui détient un str objet au lieu d'un liste objet.
Enfin, vous pouvez toujours changer ou mettre à jour l'appelable vous attribuez initialement à .default_factory de la même manière que vous le feriez avec n'importe quel attribut d'instance:
>>> dd.default_factory = str
>>> dd[[[['missing_key']
''
Dans le code ci-dessus, vous modifiez .default_factory de liste à str. Maintenant, chaque fois que vous essayez d'accéder à une clé manquante, votre valeur par défaut sera une chaîne vide ('').
Selon vos cas d'utilisation pour Python defaultdict tapez, vous devrez peut-être geler le dictionnaire une fois que vous aurez fini de le créer et de le rendre en lecture seule. Pour ce faire, vous pouvez définir .default_factory à Aucun après avoir rempli le dictionnaire. De cette façon, votre dictionnaire se comportera comme un standard dicter, ce qui signifie que vous n'aurez plus de valeurs par défaut générées automatiquement.
defaultdict contre dict.setdefault ()
Comme vous l'avez vu auparavant, dicter fournit .définir par defaut(), ce qui vous permettra d'attribuer des valeurs aux clés manquantes à la volée. En revanche, avec un defaultdict vous pouvez spécifier la valeur par défaut à l'avance lorsque vous initialisez le conteneur. Vous pouvez utiliser .définir par defaut() pour affecter les valeurs par défaut comme suit:
>>> ré = dicter()
>>> ré.définir par defaut('missing_key', [])
[]
>>> ré
'missing_key': []
Dans ce code, vous créez un dictionnaire standard, puis utilisez .définir par defaut() pour attribuer une valeur ([]) à la clé clé_ manquante, qui n'était pas encore défini.
Remarque: Vous pouvez affecter n'importe quel type d'objet Python à l'aide de .définir par defaut(). Il s'agit d'une différence importante par rapport à defaultdict si vous considérez que defaultdict accepte uniquement un appelable ou Aucun.
En revanche, si vous utilisez un defaultdict pour accomplir la même tâche, la valeur par défaut est générée à la demande chaque fois que vous essayez d'accéder ou de modifier une clé manquante. Notez qu'avec defaultdict, la valeur par défaut est générée par l'appelable que vous transmettez en amont à l'initialiseur de la classe. Voici comment ça fonctionne:
>>> de collections importation defaultdict
>>> dd = defaultdict(liste)
>>> dd[[[['missing_key']
[]
>>> dd
defaultdict (, 'missing_key': [])
Ici, vous importez d'abord le Python defaultdict taper de collections. Ensuite, vous créez un defaultdict et passer liste à .default_factory. Lorsque vous essayez d'accéder à une clé manquante, defaultdict appels internes .default_factory (), qui contient une référence à listeet attribue la valeur résultante (une valeur vide liste) à clé_ manquante.
Le code dans les deux exemples ci-dessus fait le même travail, mais le defaultdict la version est sans doute plus lisible, conviviale, Pythonic et simple.
Remarque: Un appel à un type intégré comme liste, ensemble, dicter, str, int, ou flotte renverra un objet vide ou zéro pour les types numériques.
Jetez un œil aux exemples de code suivants:
>>> liste()
[]
>>> ensemble()
ensemble([])
>>> dicter()
>>> str()
''
>>> flotte()
0,0
>>> int()
0
Dans ce code, vous appelez certains types intégrés sans arguments et obtenez un objet vide ou zéro pour les types numériques.
Enfin, en utilisant un defaultdict gérer les clés manquantes peut être plus rapide que d'utiliser dict.setdefault (). Jetez un œil à l'exemple suivant:
# Nom de fichier: exec_time.py
de collections importation defaultdict
de timeit importation timeit
animaux = [([([([('chat', 1), ('lapin', 2), ('chat', 3), ('chien', 4), ('dog', 1)]
std_dict = dict()
def_dict = defaultdict(list)
def group_with_dict():
pour animal, count dans animals:
std_dict.setdefault(animal, []).append(count)
revenir std_dict
def group_with_defaultdict():
pour animal, count dans animals:
def_dict[[[[animal].append(count)
revenir def_dict
impression(f'dict.setdefault() takes timeit(group_with_dict) seconds.')
impression(f'defaultdict takes timeit(group_with_defaultdict) seconds.')
If you run the script from your system’s command line, then you’ll get something like this:
$ python3 exec_time.py
dict.setdefault() takes 1.0281260240008123 seconds.
defaultdict takes 0.6704721650003194 seconds.
Here, you use timeit.timeit() to measure the execution time of group_with_dict() et group_with_defaultdict(). These functions perform equivalent actions, but the first uses dict.setdefault(), and the second uses a defaultdict. The time measure will depend on your current hardware, but you can see here that defaultdict is faster than dict.setdefault(). This difference can become more important as the dataset gets larger.
Additionally, you need to consider that creating a regular dict can be faster than creating a defaultdict. Take a look at this code:
>>> de timeit import timeit
>>> de collections import defaultdict
>>> impression(f'dict() takes timeit(dict) seconds.')
dict() takes 0.08921320698573254 seconds.
>>> impression(f'defaultdict() takes timeit(defaultdict) seconds.')
defaultdict() takes 0.14101867799763568 seconds.
This time, you use timeit.timeit() to measure the execution time of dict et defaultdict instantiation. Notice that creating a dict takes almost half the time of creating a defaultdict. This might not be a problem if you consider that, in real-world code, you normally instantiate defaultdict only once.
Also notice that, by default, timeit.timeit() will run your code a million times. That’s the reason for defining std_dict et def_dict out of the scope of group_with_dict() et group_with_defaultdict() dans exec_time.py. Otherwise, the time measure will be affected by the instantiation time of dict et defaultdict.
At this point, you may have an idea of when to use a defaultdict rather than a regular dict. Here are three things to take into account:
-
If your code is heavily base on dictionaries and you’re dealing with missing keys all the time, then you should consider using a
defaultdictrather than a regulardict. -
If your dictionary items need to be initialized with a constant default value, then you should consider using a
defaultdictinstead of adict. -
If your code relies on dictionaries for aggregating, accumulating, counting, or grouping values, and performance is a concern, then you should consider using a
defaultdict.
You can consider the above guidelines when deciding whether to use a dict ou un defaultdict.
defaultdict.__missing__()
Behind the scenes, the Python defaultdict type works by calling .default_factory to supply default values to missing keys. The mechanism that makes this possible is .__missing__(), a special method supported by all the standard mapping types, including dict et defaultdict.
Remarque: Note that .__missing__() is automatically called by .__getitem__() to handle missing keys and that .__getitem__() is automatically called by Python at the same time for subscription operations like ré[key].
So, how does .__missing__() work? If you set .default_factory à Aucun, then .__missing__() raises a KeyError with the key as an argument. Autrement, .default_factory is called without arguments to provide a default value for the given key. Cette value is inserted into the dictionary and finally returned. If calling .default_factory raises an exception, then the exception is propagated unchanged.
The following code shows a viable Python implementation for .__missing__():
1 def __missing__(self, key):
2 si self.default_factory est Aucun:
3 raise KeyError(key)
4 si key ne pas dans self:
5 self[[[[key] = self.default_factory()
6 revenir self[[[[key]
Here’s what this code does:
- In line 1, you define the method and its signature.
- In lines 2 and 3, you test to see if
.default_factoryestAucun. If so, then you raise aKeyErrorwith thekeyas an argument. - In lines 4 and 5, you check if the
keyis not in the dictionary. If it’s not, then you call.default_factoryand assign its return value to thekey. - In line 6, you return the
keyas expected.
Keep in mind that the presence of .__missing__() in a mapping has no effect on the behavior of other methods that look up keys, such as .get() ou .__contains__(), which implements the dans operator. C'est parce que .__missing__() is only called by .__getitem__() when the requested key is not found in the dictionary. Peu importe .__missing__() returns or raises is then returned or raised by .__getitem__().
Now that you’ve covered an alternative Python implementation for .__missing__(), it would be a good exercise to try to emulate defaultdict with some Python code. That’s what you’ll be doing in the next section.
Emulating the Python defaultdict Type
In this section, you’ll be coding a Python class that will behave much like a defaultdict. To do that, you’ll subclass collections.UserDict and then add .__missing__(). Also, you need to add an instance attribute called .default_factory, which will hold the callable for generating default values on demand. Here’s a piece of code that emulates most of the behavior of the Python defaultdict type:
1 import collections
2
3 class my_defaultdict(collections.UserDict):
4 def __init__(self, default_factory=Aucun, *args, **kwargs):
5 super().__init__(*args, **kwargs)
6 si ne pas callable(default_factory) et default_factory est ne pas Aucun:
sept raise TypeError('first argument must be callable or None')
8 self.default_factory = default_factory
9
dix def __missing__(self, key):
11 si self.default_factory est Aucun:
12 raise KeyError(key)
13 si key ne pas dans self:
14 self[[[[key] = self.default_factory()
15 revenir self[[[[key]
Here’s how this code works:
-
In line 1, you import
collectionsto get access toUserDict. -
In line 3, you create a class that subclasses
UserDict. -
In line 4, you define the class initializer
.__init__(). This method takes an argument calleddefault_factoryto hold the callable that you’ll use to generate the default values. Notice thatdefault_factorydefaults toAucun, just like in adefaultdict. You also need the*argset**kwargsfor emulating the normal behavior of a regulardict. -
In line 5, you call the superclass
.__init__(). This means that you’re callingUserDict.__init__()and passing*argset**kwargsto it. -
In line 6, you first check if
default_factoryis a valid callable object. In this case, you usecallable(object), which is a built-in function that returnsTruesiobjectappears to be a callable and otherwise returnsFalse. This check ensures that you can call.default_factory()if you need to generate a defaultvaluefor any missingkey. Then, you check if.default_factoryis notAucun. -
In line 7, you raise a
TypeErrorjust like a regulardictwould do ifdefault_factoryestAucun. -
In line 8, you initialize
.default_factory. -
In line 10, you define
.__missing__(), which is implemented as you saw before. Recall that.__missing__()is automatically called by.__getitem__()when a givenkeyis not in a dictionary.
If you feel in the mood to read some C code, then you can take a look at the full code for the Python defaultdict Type in the CPython source code.
Now that you’ve finished coding this class, you can test it by putting the code into a Python script called my_dd.py and importing it from an interactive session. Here’s an example:
>>> de my_dd import my_defaultdict
>>> dd_one = my_defaultdict(list)
>>> dd_one
>>> dd_one[[[['missing']
[]
>>> dd_one
'missing': []
>>> dd_one.default_factory = int
>>> dd_one[[[['another_missing']
0
>>> dd_one
'missing': [], 'another_missing': 0
>>> dd_two = my_defaultdict(Aucun)
>>> dd_two[[[['missing']
Traceback (most recent call last):
Fichier "" , line 1, dans
dd_two[[[['missing']
File "/home/user/my_dd.py", line 10,
in __missing__
raise KeyError(key)
KeyError: 'missing'
Here, you first import my_defaultdict de my_dd. Then, you create an instance of my_defaultdict and pass list à .default_factory. If you try to get access to a key with a subscription operation, like dd_one['missing'], then .__getitem__() is automatically called by Python. If the key is not in the dictionary, then .__missing__() is called, which generates a default value by calling .default_factory().
You can also change the callable assigned to .default_factory using a normal assignment operation like in dd_one.default_factory = int. Finally, if you pass Aucun à .default_factory, then you’ll get a KeyError when trying to retrieve a missing key.
Remarque: The behavior of a defaultdict is essentially the same as this Python equivalent. However, you’ll soon note that your Python implementation doesn’t print as a real defaultdict but as a standard dict. You can modify this detail by overriding .__str__() et .__repr__().
You may be wondering why you subclass collections.UserDict instead of a regular dict pour cet exemple. The main reason for this is that subclassing built-in types can be error-prone because the C code of the built-ins doesn’t seem to consistently call special methods overridden by the user.
Here’s an example that shows some issues that you can face when subclassing dict:
>>> class MyDict(dict):
... def __setitem__(self, key, value):
... super().__setitem__(key, Aucun)
...
>>> my_dict = MyDict(premier=1)
>>> my_dict
'first': 1
>>> my_dict[[[['second'] = 2
>>> my_dict
'first': 1, 'second': None
>>> my_dict.setdefault('third', 3)
3
>>> my_dict
'first': 1, 'second': None, 'third': 3
In this example, you create MyDict, which is a class that subclasses dict. Your implementation of .__setitem__() always sets values to Aucun. If you create an instance of MyDict and pass a keyword argument to its initializer, then you’ll notice the class is not calling your .__setitem__() to handle the assignment. You know that because the key premier wasn’t assigned Aucun.
By contrast, if you run a subscription operation like my_dict['second'] = 2, then you’ll notice that seconde is set to Aucun rather than to 2. So, this time you can say that subscription operations call your custom .__setitem__(). Finally, notice that .setdefault() doesn’t call .__setitem__() either, because your troisième key ends up with a value of 3.
UserDict doesn’t inherit from dict but simulates the behavior of a standard dictionary. The class has an internal dict instance called .data, which is used to store the content of the dictionary. UserDict is a more reliable class when it comes to creating custom mappings. If you use UserDict, then you’ll be avoiding the issues you saw before. To prove this, go back to the code for my_defaultdict and add the following method:
1 class my_defaultdict(collections.UserDict):
2 # Snip
3 def __setitem__(self, key, value):
4 impression('__setitem__() gets called')
5 super().__setitem__(key, Aucun)
Here, you add a custom .__setitem__() that calls the superclass .__setitem__(), which always sets the value to Aucun. Update this code in your script my_dd.py and import it from an interactive session as follows:
>>> de my_dd import my_defaultdict
>>> my_dict = my_defaultdict(list, premier=1)
__setitem__() gets called
>>> my_dict
'first': None
>>> my_dict[[[['second'] = 2
__setitem__() gets called
>>> my_dict
'first': None, 'second': None
In this case, when you instantiate my_defaultdict and pass premier to the class initializer, your custom __setitem__() gets called. Also, when you assign a value to the key seconde, __setitem__() gets called as well. You now have a my_defaultdict that consistently calls your custom special methods. Notice that all the values in the dictionary are equal to Aucun maintenant.
Passing Arguments to .default_factory
As you saw earlier, .default_factory must be set to a callable object that takes no argument and returns a value. This value will be used to supply a default value for any missing key in the dictionary. Even when .default_factory shouldn’t take arguments, Python offers some tricks that you can use if you need to supply arguments to it. In this section, you’ll cover two Python tools that can serve this purpose:
lambdafunctools.partial()
With these two tools, you can add extra flexibility to the Python defaultdict type. For example, you can initialize a defaultdict with a callable that takes an argument and, after some processing, you can update the callable with a new argument to change the default value for the keys you’ll create from this point on.
Using lambda
A flexible way to pass arguments to .default_factory is to use lambda. Suppose you want to create a function to generate default values in a defaultdict. The function does some processing and returns a value, but you need to pass an argument for the function to work correctly. Here’s an example:
>>> def factory(arg):
... # Do some processing here...
... result = arg.upper()
... revenir result
...
>>> def_dict = defaultdict(lambda: factory('default value'))
>>> def_dict[[[['missing']
'DEFAULT VALUE'
In the above code, you create a function called factory(). The function takes an argument, does some processing, and returns the final result. Then, you create a defaultdict and use lambda to pass the string 'default value' à factory(). When you try to get access to a missing key, the following steps are run:
- The dictionary
def_dictcalls its.default_factory, which holds a reference to alambdafunction. - le
lambdafunction gets called and returns the value that results from callingfactory()with'default value'as an argument.
If you’re working with def_dict and suddenly need to change the argument to factory(), then you can do something like this:
>>> def_dict.default_factory = factory('another default value')
>>> def_dict[[[['another_missing']
'ANOTHER DEFAULT VALUE'
This time, factory() takes a new string argument ('another default value'). From now on, if you try to access or modify a missing key, then you’ll get a new default value, which is the string 'ANOTHER DEFAULT VALUE'.
Finally, you can possibly face a situation where you need a default value that’s different from 0 ou []. In this case, you can also use lambda à generate a different default value. For example, suppose you have a list of integer numbers, and you need to calculate the cumulative product of each number. Then, you can use a defaultdict de même que lambda comme suit:
>>> de collections import defaultdict
>>> lst = [[[[1, 1, 2, 1, 2, 2, 3, 4, 3, 3, 4, 4]
>>> def_dict = defaultdict(lambda: 1)
>>> pour nombre dans lst:
... def_dict[[[[nombre] *= nombre
...
>>> def_dict
defaultdict(<function at 0x...70>, 1: 1, 2: 8, 3: 27, 4: 64)
Here, you use lambda to supply a default value of 1. With this initial value, you can calculate the cumulative product of each number in lst. Notice that you can’t get the same result using int because the default value returned by int is always 0, which is not a good initial value for the multiplication operations you need to perform here.
Using functools.partial()
functools.partial(func, *args, **keywords) is a function that returns a partial object. When you call this object with the positional arguments (args) and keyword arguments (keywords), it behaves similar to when you call func(*args, **keywords). You can take advantage of this behavior of partial() and use it to pass arguments to .default_factory in a Python defaultdict. Here’s an example:
>>> def factory(arg):
... # Do some processing here...
... result = arg.upper()
... revenir result
...
>>> de functools import partial
>>> def_dict = defaultdict(partial(factory, 'default value'))
>>> def_dict[[[['missing']
'DEFAULT VALUE'
>>> def_dict.default_factory = partial(factory, 'another default value')
>>> def_dict[[[['another_missing']
'ANOTHER DEFAULT VALUE'
Here, you create a Python defaultdict and use partial() to supply an argument to .default_factory. Notice that you can also update .default_factory to use another argument for the callable factory(). This kind of behavior can add a lot of flexibility to your defaultdict objects.
Conclusion
The Python defaultdict type is a dictionary-like data structure provided by the Python standard library in a module called collections. The class inherits from dict, and its main added functionality is to supply default values for missing keys. In this tutorial, you’ve learned how to use the Python defaultdict type for handling the missing keys in a dictionary.
You’re now able to:
- Create and use a Python
defaultdictto handle missing keys - Solve real-world problems related to grouping, counting, and accumulating operations
- Know the implementation differences between
defaultdictetdict - Decide when and why to use a Python
defaultdictrather than a standarddict
The Python defaultdict type is a convenient and efficient data structure that’s designed to help you out when you’re dealing with missing keys in a dictionary. Give it a try and make your code faster, more readable, and more Pythonic!
[ad_2]