209 votes

Cas d'utilisation de la méthode 'setdefault' du dictionnaire

L'ajout de collections.defaultdict en Python 2.5 a grandement réduit le besoin de la méthode setdefault de dict. Cette question est pour notre éducation collective :

  1. Pour quoi setdefault est-il encore utile aujourd'hui en Python 2.6/2.7 ?
  2. Quels cas d'utilisation populaires de setdefault ont été remplacés par collections.defaultdict ?

229voto

Jochen Ritzel Points 42916

Vous pourriez dire que defaultdict est utile pour définir des valeurs par défaut avant de remplir le dictionnaire et que setdefault est utile pour définir des valeurs par défaut pendant ou après le remplissage du dictionnaire.

Probablement le cas d'utilisation le plus courant : regrouper des éléments (dans des données non triées, sinon utilisez itertools.groupby)

# vraiment verbeux
new = {}
for (key, value) in data:
    if key in new:
        new[key].append( value )
    else:
        new[key] = [value]

# facile avec setdefault
new = {}
for (key, value) in data:
    group = new.setdefault(key, []) # la clé pourrait déjà exister
    group.append( value )

# encore plus simple avec defaultdict 
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
    new[key].append( value ) # toutes les clés ont déjà une valeur par défaut

Parfois, vous souhaitez vous assurer que des clés spécifiques existent après la création d'un dictionnaire. defaultdict ne fonctionne pas dans ce cas, car il crée des clés uniquement lors de l'accès explicite. Pensez à utiliser quelque chose de semblable à HTTP avec de nombreux en-têtes - certains sont facultatifs, mais vous voulez des valeurs par défaut pour eux :

headers = parse_headers( msg ) # analyser le message, obtenir un dictionnaire
# maintenant ajoutez tous les en-têtes facultatifs
for nom_en_tete, valeur_par_defaut in en_tetes_optionnels:
    headers.setdefault( nom_en_tete, valeur_par_defaut )

1 votes

En effet, à mon avis, c'est le principal cas d'utilisation pour le remplacement par defaultdict. Pouvez-vous donner un exemple de ce que vous voulez dire dans le premier paragraphe?

0 votes

Je ne ferais certainement pas ça pour le dernier exemple. Pourquoi ne pas utiliser headers = dict(optional_headers); headers.update(parse_headers(msg)) ou même un defaultdict pour les en-têtes avant d'utiliser update ?

2 votes

Muhammad Alkarouri: Ce que vous faites en premier lieu est de copier le dictionnaire puis de remplacer certains des éléments. Je fais ça beaucoup aussi et je suppose que c'est en fait l'idiome que la plupart préfèrent par rapport à setdefault. Un defaultdict d'autre part ne fonctionnerait pas si toutes les valeurs par défaut ne sont pas égales (certaines sont 0 et d'autres sont []).

35voto

Matt Joiner Points 29194

Je utilise communément setdefault pour les dictionnaires d'arguments de mots-clés, comme dans cette fonction :

def notify(self, level, *pargs, **kwargs):
    kwargs.setdefault("persist", level >= DANGER)
    self.__defcon.set(level, **kwargs)
    try:
        kwargs.setdefault("name", self.client.player_entity().name)
    except pytibia.PlayerEntityNotFound:
        pass
    return _notify(level, *pargs, **kwargs)

C'est idéal pour ajuster les arguments dans des wrappers autour des fonctions qui utilisent des arguments de mots-clés.

18voto

David Kanarek Points 8816

defaultdict est génial lorsque la valeur par défaut est statique, comme une nouvelle liste, mais pas tellement si elle est dynamique.

Par exemple, j'ai besoin d'un dictionnaire pour mapper les chaînes de caractères sur des entiers uniques. defaultdict(int) utilisera toujours 0 comme valeur par défaut. De même, defaultdict(intGen()) produit toujours 1.

À la place, j'ai utilisé un dictionnaire régulier :

nextID = intGen()
myDict = {}
pour beaucoup de choses compliquées :
    # choses qui génèrent des chaînes de caractères imprévisibles, peut-être déjà vues
    strID = myDict.setdefault(myStr, nextID())

Notez que dict.get(key, nextID()) est insuffisant car j'ai besoin de pouvoir référer à ces valeurs plus tard également.

intGen est une petite classe que j'ai créée qui incrémente automatiquement un entier et renvoie sa valeur :

class intGen:
    def __init__(self):
        self.i = 0

    def __call__(self):
        self.i += 1
    return self.i

Si quelqu'un a une manière de faire cela avec defaultdict, j'adorerais le voir.

0 votes

Pour un moyen de le faire avec (une sous-classe de) defaultdict, voir cette question: stackoverflow.com/questions/2912231/…

8 votes

Vous pourriez remplacer intGen par itertools.count().next.

9 votes

Le valeur de nextID() va être incrémentée chaque fois que myDict.setdefault() est appelé, même si la valeur qu'il renvoie n'est pas utilisée comme strID. Cela semble gaspilleur d'une certaine manière et illustre l'une des choses que je n'aime pas à propos de setdefault() en général - à savoir qu'il évalue toujours son argument default que celui-ci soit réellement utilisé ou non.

11voto

AndyGeek Points 570

J'utilise setdefault() lorsque je veux une valeur par défaut dans un OrderedDict. Il n'existe pas de collection Python standard qui le fait, mais il y a des moyens d'implémenter une telle collection.

5voto

Muhammad Alkarouri Points 8463

Théoriquement parlant, setdefault serait toujours pratique si vous voulez parfois définir une valeur par défaut et parfois non. Dans la vraie vie, je n'ai pas encore rencontré un tel cas d'utilisation.

Cependant, un cas d'utilisation intéressant provient de la bibliothèque standard (Python 2.6, _threadinglocal.py) :

>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]

Je dirais que l'utilisation de __dict__.setdefault est un cas assez utile.

Édition : Comme il se trouve, c'est le seul exemple dans la bibliothèque standard et il est dans un commentaire. Donc peut-être ce n'est pas suffisant pour justifier l'existence de setdefault. Néanmoins, voici une explication :

Les objets stockent leurs attributs dans l'attribut __dict__. Comme il se trouve, l'attribut __dict__ est modifiable à tout moment après la création de l'objet. C'est aussi un dictionnaire et non un defaultdict. Il n'est pas sensé pour les objets en général d'avoir __dict__ comme un defaultdict car cela ferait en sorte que chaque objet ait tous les identifiants légaux comme attributs. Donc je ne vois pas de changement possible dans les objets Python qui se débarrasseraient de __dict__.setdefault, à part le supprimer complètement s'il était jugé inutile.

1 votes

Pourriez-vous élaborer - qu'est-ce qui rend _dict.setdefault particulièrement utile ?

1 votes

@Eli: Je pense que le point est que __dict__ est par implémentation un dict, pas un defaultdict.

1 votes

D'accord. Ça ne me dérange pas que setdefault reste en Python, mais c'est curieux de voir que c'est maintenant presque inutile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X