Vous pourriez dire que defaultdict
c'est utile pour les paramètres par défaut avant de remplir le dict et setdefault
est utile pour définir les valeurs par défaut pendant ou après le remplissage du dict .
Probablement le cas d'utilisation le plus courant: regrouper des éléments (dans des données non triées, sinon utiliser itertools.groupby
)
new = {}
for (key, value) in data:
if key in new:
new[key].append( value )
else:
new[key] = [value]
new = {}
for (key, value) in data:
group = new.setdefault(key, [])
group.append( value )
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append( value )
Parfois, vous voulez vous assurer que des clés spécifiques existent après la création d'un dict. defaultdict
ne fonctionne pas dans ce cas, car il crée uniquement des clés sur un accès explicite. Pensez que vous utilisez quelque chose de HTTP-ish avec de nombreux en-têtes - certains sont facultatifs, mais vous voulez des valeurs par défaut pour eux:
headers = parse_headers( msg )
for headername, defaultvalue in optional_headers:
headers.setdefault( headername, defaultvalue )
defaultdict
. Pouvez-vous donner un exemple de ce que vous voulez dire dans le premier paragraphe?setdefault
. Adefaultdict
d'un autre côté ne fonctionnerait pas si tous lesdefaultvalues
sont égaux (c'est-à-dire que certains le sont0
et certains le sont[]
).headers = dict(optional_headers)
. Pour le cas où les valeurs par défaut ne sont pas toutes égales. Et le résultat final est le même que si vous obtenez d'abord les en-têtes HTTP, puis définissez les valeurs par défaut pour ceux que vous n'avez pas obtenus. Et il est tout à fait utilisable si vous l'avez déjàoptional_headers
. Essayez mon code en 2 étapes et comparez-le au vôtre, et vous verrez ce que je veux dire.new.setdefault(key, []).append(value)
defaultdict
soit encore meilleure quesetdefault
(alors où est le cas d'utilisation maintenant?). En outre,ChainMap
serait mieux gérer l'http
exemple, l'OMI.J'utilise couramment
setdefault
pour les dictées d'argument de mot-clé, 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 peaufiner les arguments dans les wrappers autour des fonctions qui acceptent des arguments de mot-clé.
la source
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 des chaînes sur des entiers uniques.
defaultdict(int)
utilisera toujours 0 comme valeur par défaut. De même,defaultdict(intGen())
produit toujours 1.Au lieu de cela, j'ai utilisé un dict régulier:
nextID = intGen() myDict = {} for lots of complicated stuff: #stuff that generates unpredictable, possibly already seen str strID = myDict.setdefault(myStr, nextID())
Notez que
dict.get(key, nextID())
c'est insuffisant car je dois également pouvoir me référer à ces valeurs plus tard.intGen
est une petite classe que je construis qui incrémente automatiquement un int 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 un moyen de faire cela avec,
defaultdict
j'aimerais le voir.la source
intGen
paritertools.count().next
.nextID()
La valeur de s va être incrémentée à chaquemyDict.setdefault()
appel, même si la valeur qu'elle renvoie n'est pas utilisée comme unstrID
. Cela semble en quelque sorte un gaspillage et illustre l'une des choses que je n'aime passetdefault()
en général - à savoir qu'il évalue toujours sondefault
argument s'il est réellement utilisé ou non.defaultdict
:myDict = defaultdict(lambda: nextID())
. Plus tard,strID = myDict[myStr]
dans la boucle.myDict = defaultdict(nextID)
?Comme la plupart des réponses indiquent
setdefault
oudefaultdict
vous permet de définir une valeur par défaut lorsqu'une clé n'existe pas. Cependant, je voudrais signaler une petite mise en garde concernant les cas d'utilisation desetdefault
. Lorsque l'interpréteur Python s'exécute,setdefault
il évaluera toujours le deuxième argument de la fonction même si la clé existe dans le dictionnaire. Par exemple:In: d = {1:5, 2:6} In: d Out: {1: 5, 2: 6} In: d.setdefault(2, 0) Out: 6 In: d.setdefault(2, print('test')) test Out: 6
Comme vous pouvez le voir,
print
a également été exécuté même si 2 existait déjà dans le dictionnaire. Cela devient particulièrement important si vous prévoyez d'utilisersetdefault
par exemple pour une optimisation commememoization
. Si vous ajoutez un appel de fonction récursive comme deuxième argument àsetdefault
, vous n'obtiendrez aucune performance car Python appellerait toujours la fonction de manière récursive.Puisque la mémorisation a été mentionnée, une meilleure alternative est d'utiliser le décorateur functools.lru_cache si vous envisagez d'améliorer une fonction avec la mémorisation. lru_cache gère mieux les exigences de mise en cache pour une fonction récursive.
la source
J'utilise
setdefault()
quand je veux une valeur par défaut dans un fichierOrderedDict
. Il n'existe pas de collection Python standard qui fasse les deux, mais il existe des moyens d'implémenter une telle collection.la source
Comme l'a dit Muhammad, il existe des situations dans lesquelles vous ne souhaitez parfois définir une valeur par défaut. Un bon exemple de ceci est une structure de données qui est d'abord remplie, puis interrogée.
Considérez un trie. Lors de l'ajout d'un mot, si un sous-nœud est nécessaire mais pas présent, il doit être créé pour étendre le trie. Lors de l'interrogation de la présence d'un mot, un sous-nœud manquant indique que le mot n'est pas présent et qu'il ne doit pas être créé.
Un defaultdict ne peut pas faire cela. Au lieu de cela, un dict régulier avec les méthodes get et setdefault doit être utilisé.
la source
Théoriquement parlant, cela
setdefault
serait toujours pratique si vous souhaitez parfois définir une valeur par défaut et parfois non. Dans la vraie vie, je n'ai pas 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
__dict__.setdefault
est un cas assez utile.Edit : En l'occurrence, c'est le seul exemple de la bibliothèque standard et c'est dans un commentaire. Il se peut donc que ce ne soit pas suffisant pour justifier l’existence de
setdefault
. Pourtant, voici une explication:Les objets stockent leurs attributs dans l'
__dict__
attribut. En l'occurrence, l'__dict__
attribut est accessible en écriture à tout moment après la création de l'objet. C'est aussi un dictionnaire pas undefaultdict
. Il n'est pas judicieux pour les objets dans le cas général d'avoir__dict__
comme undefaultdict
car cela rendrait chaque objet ayant tous les identifiants légaux comme attributs. Je ne peux donc pas prévoir de changement dans la__dict__.setdefault
suppression des objets Python , à part de les supprimer complètement si cela n'a pas été jugé utile.la source
__dict__
par l'implémentation adict
, pas adefaultdict
.setdefault
rester en Python, mais c'est curieux de voir que c'est maintenant presque inutile.setdefault
rend explicite que vous affectez à un dict via une clé qui peut exister ou non, et si elle n'existe pas, vous voulez qu'elle soit créée avec une valeur par défaut: par exempled.setdefault(key,[]).append(value)
. Ailleurs dans le programme, vous faitesalist=d[k]
où k est calculé, et vous voulez une exception levée si k n'est pas dans d (ce qui avec un defaultdict pourrait exigerassert k in d
ou mêmeif not ( k in d): raise KeyError
J'ai réécrit la réponse acceptée et la facilité pour les débutants.
#break it down and understand it intuitively. new = {} for (key, value) in data: if key not in new: new[key] = [] # this is core of setdefault equals to new.setdefault(key, []) new[key].append(value) else: new[key].append(value) # easy with setdefault new = {} for (key, value) in data: group = new.setdefault(key, []) # it is new[key] = [] group.append(value) # even simpler with defaultdict new = defaultdict(list) for (key, value) in data: new[key].append(value) # all keys have a default value of empty list []
De plus, j'ai classé les méthodes comme référence:
dict_methods_11 = { 'views':['keys', 'values', 'items'], 'add':['update','setdefault'], 'remove':['pop', 'popitem','clear'], 'retrieve':['get',], 'copy':['copy','fromkeys'],}
la source
Un inconvénient de
defaultdict
overdict
(dict.setdefault
) est qu'undefaultdict
objet crée un nouvel élément CHAQUE FOIS, une clé non existante est donnée (par exemple avec==
,print
). De plus, ladefaultdict
classe est généralement beaucoup moins courante que ladict
classe, il est plus difficile de la sérialiser IME.Fonctions PS IMO | méthodes non destinées à muter un objet, ne doivent pas muter un objet.
la source
defaultdict(lambda l=[]: l)
place.Voici quelques exemples de setdefault pour montrer son utilité:
""" d = {} # To add a key->value pair, do the following: d.setdefault(key, []).append(value) # To retrieve a list of the values for a key list_of_values = d[key] # To remove a key->value pair is still easy, if # you don't mind leaving empty lists behind when # the last value for a given key is removed: d[key].remove(value) # Despite the empty lists, it's still possible to # test for the existance of values easily: if d.has_key(key) and d[key]: pass # d has some values for key # Note: Each value can exist multiple times! """ e = {} print e e.setdefault('Cars', []).append('Toyota') print e e.setdefault('Motorcycles', []).append('Yamaha') print e e.setdefault('Airplanes', []).append('Boeing') print e e.setdefault('Cars', []).append('Honda') print e e.setdefault('Cars', []).append('BMW') print e e.setdefault('Cars', []).append('Toyota') print e # NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota'] e['Cars'].remove('Toyota') print e # NOTE: it's still true that ('Toyota' in e['Cars'])
la source
J'utilise fréquemment setdefault quand, obtenez ceci, en définissant une valeur par défaut (!!!) dans un dictionnaire; un peu communément le dictionnaire os.environ:
# Set the venv dir if it isn't already overridden: os.environ.setdefault('VENV_DIR', '/my/default/path')
Moins succinctement, cela ressemble à ceci:
# Set the venv dir if it isn't already overridden: if 'VENV_DIR' not in os.environ: os.environ['VENV_DIR'] = '/my/default/path')
Il est à noter que vous pouvez également utiliser la variable résultante:
venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')
Mais c'est moins nécessaire qu'avant l'existence des defaultdicts.
la source
Un autre cas d'utilisation que je ne pense pas a été mentionné ci-dessus. Parfois, vous gardez un cache des objets par leur identifiant où l'instance principale est dans le cache et vous voulez définir le cache en cas d'absence.
return self.objects_by_id.setdefault(obj.id, obj)
C'est utile lorsque vous souhaitez toujours conserver une seule instance par identifiant distinct, quelle que soit la manière dont vous obtenez un objet à chaque fois. Par exemple, lorsque les attributs d'objet sont mis à jour en mémoire et que l'enregistrement dans le stockage est différé.
la source
Un cas d'utilisation très important sur
dict.setdefault()
lequel je viens de tomber: il est idéal pour le code multi-thread lorsque vous ne voulez qu'un seul objet canonique (par opposition à plusieurs objets qui se trouvent être égaux).Par exemple,
(Int)Flag
Enum dans Python 3.6.0 a un bogue : si plusieurs threads sont en concurrence pour un(Int)Flag
membre composite , il peut y en avoir plus d'un:from enum import IntFlag, auto import threading class TestFlag(IntFlag): one = auto() two = auto() three = auto() four = auto() five = auto() six = auto() seven = auto() eight = auto() def __eq__(self, other): return self is other def __hash__(self): return hash(self.value) seen = set() class cycle_enum(threading.Thread): def run(self): for i in range(256): seen.add(TestFlag(i)) threads = [] for i in range(8): threads.append(cycle_enum()) for t in threads: t.start() for t in threads: t.join() len(seen) # 272 (should be 256)
La solution consiste à utiliser
setdefault()
comme dernière étape de sauvegarde du membre composite calculé - si un autre a déjà été enregistré, il est utilisé à la place du nouveau, garantissant des membres Enum uniques.la source
[Modifier] Très faux! Le setdefault déclencherait toujours long_computation, Python étant impatient.
Développant la réponse de Tuttle. Pour moi, le meilleur cas d'utilisation est le mécanisme de cache. Au lieu de:
if x not in memo: memo[x]=long_computation(x) return memo[x]
qui consomme 3 lignes et 2 ou 3 recherches,
j'écrirais volontiers:return memo.setdefault(x, long_computation(x))
la source
long_computation(x)
n'est appelé que six not in memo
. Alors que dans le second,long_computation(x)
est toujours appelé. Seule l'affectation est conditionnelle, le code équivalent àsetdefault
ressemblerait à:v = long_computation(x)
/if x not in memo:
/memo[x] = v
.J'aime la réponse donnée ici:
http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html
En bref, la décision (dans les applications non critiques pour les performances) doit être prise en fonction de la manière dont vous souhaitez gérer la recherche de clés vides en aval (à savoir
KeyError
par rapport à la valeur par défaut).la source
Le cas d'utilisation différent
setdefault()
est celui où vous ne souhaitez pas écraser la valeur d'une clé déjà définie.defaultdict
écrase, alors quesetdefault()
ne le fait pas. Pour les dictionnaires imbriqués, il est plus fréquent que vous souhaitiez définir une valeur par défaut uniquement si la clé n'est pas encore définie, car vous ne souhaitez pas supprimer le sous-dictionnaire actuel. C'est à ce moment que vous utilisezsetdefault()
.Exemple avec
defaultdict
:>>> from collection import defaultdict() >>> foo = defaultdict() >>> foo['a'] = 4 >>> foo['a'] = 2 >>> print(foo) defaultdict(None, {'a': 2})
setdefault
n'écrase pas:>>> bar = dict() >>> bar.setdefault('a', 4) >>> bar.setdefault('a', 2) >>> print(bar) {'a': 4}
la source