method functions dictionaries dict python dictionary setdefault

functions - dictionary method python



Casos de uso para el método dict ''setdefault'' (16)

Aquí hay algunos ejemplos de setdefault para mostrar su utilidad:

""" 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 adición de collections.defaultdict en Python 2.5 redujo en gran medida la necesidad del método setdefault dict . Esta pregunta es para nuestra educación colectiva:

  1. ¿ setdefault qué sirve setdefault todavía, hoy en Python 2.6 / 2.7?
  2. ¿Qué casos de uso popular de setdefault se reemplazaron con collections.defaultdict ?

Comúnmente uso setdefault para los setdefault de argumento de palabra clave, como en esta función:

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)

Es genial para ajustar argumentos en envoltorios alrededor de funciones que toman argumentos de palabra clave.


Como dijo Muhammad, hay situaciones en las que a veces solo deseas establecer un valor predeterminado. Un gran ejemplo de esto es una estructura de datos que primero se rellena y se consulta.

Considera un trie. Al agregar una palabra, si se necesita un subnodo pero no está presente, se debe crear para extender el trie. Al consultar la presencia de una palabra, un subnodo faltante indica que la palabra no está presente y no debe crearse.

Un incumplimiento no puede hacer esto. En su lugar, se debe usar un dict regular con los métodos get y setdefault.


Cuando el valor predeterminado requerido no es siempre el mismo, o solo se desea para claves específicas, pero se prefiere no tener uno para los demás, se puede considerar el uso de setdefault :

d = {} ... # `i` should default to zero i = d.setdefault(key, 0) ... # `s` should default to an empty string s = d.setdefault(key, '''') ...

d = {} ... # v should always default to a list v = d.setdefault(key, []) ... try: # EAFP, but I need the dict to raise a KeyError if the key is not found. w = d[k2] except KeyError: ... ...


El caso de uso diferente para setdefault() es cuando no desea sobreescribir el valor de una clave ya establecida. defaultdict sobrescribe, mientras que setdefault() no lo hace. En el caso de los diccionarios anidados, es más frecuente que desee establecer un valor predeterminado solo si la clave aún no está establecida, porque no desea eliminar el presente sub Diccionario. Esto es cuando usas setdefault() .

Ejemplo con defaultdict :

>>> from collection import defaultdict() >>> foo = defaultdict() >>> foo[''a''] = 4 >>> foo[''a''] = 2 >>> print(foo) defaultdict(None, {''a'': 2})

setdefault no sobrescribe:

>>> bar = dict() >>> bar.setdefault(''a'', 4) >>> bar.setdefault(''a'', 2) >>> print(bar) {''a'': 4}


La lógica de dict.get es:

if key in a_dict: value = a_dict[key] else: value = default_value

Tome un ejemplo:

In [72]: a_dict = {''mapping'':[''dict'', ''OrderedDict''], ''array'':[''list'', ''tuple'']} In [73]: a_dict.get(''string'', [''str'', ''bytes'']) Out[73]: [''str'', ''bytes''] In [74]: a_dict.get(''array'', [''str'', ''byets'']) Out[74]: [''list'', ''tuple'']

El mecamismo de setdefault es:

levels = [''master'', ''manager'', ''salesman'', ''accountant'', ''assistant''] #group them by the leading letter group_by_leading_letter = {} # the logic expressed by obvious if condition for level in levels: leading_letter = level[0] if leading_letter not in group_by_leading_letter: group_by_leading_letter[leading_letter] = [level] else: group_by_leading_letter[leading_letter].append(word) In [80]: group_by_leading_letter Out[80]: {''a'': [''accountant'', ''assistant''], ''m'': [''master'', ''manager''], ''s'': [''salesman'']}

El método setdefault dict es precisamente para este propósito. El ciclo for anterior se puede reescribir como sigue:

In [87]: for level in levels: ...: leading = level[0] ...: group_by_leading_letter.setdefault(leading,[]).append(level) Out[80]: {''a'': [''accountant'', ''assistant''], ''m'': [''master'', ''manager''], ''s'': [''salesman'']}

Es muy simple, significa que una lista no nula agrega un elemento o una lista nula anexa un elemento.

El defaultdict , que hace que esto sea aún más fácil. Para crear uno, pasas un tipo o función para generar el valor predeterminado para cada ranura en el dict:

from collections import defualtdict group_by_leading_letter = defaultdict(list) for level in levels: group_by_leading_letter[level[0]].append(level)



Otro caso de uso que no creo que se haya mencionado anteriormente. A veces, conservas un caché de objetos con su id. Donde la instancia principal está en el caché y quieres configurar el caché cuando falta.

return self.objects_by_id.setdefault(obj.id, obj)

Eso es útil cuando siempre desea mantener una sola instancia por identificación distinta, sin importar cómo obtenga un obj cada vez. Por ejemplo, cuando los atributos del objeto se actualizan en la memoria y se pospone el almacenamiento en el almacenamiento.


Se podría decir que el valor predeterminado es útil para los valores predeterminados de configuración antes de llenar el dict y setdefault es útil para establecer los valores predeterminados mientras o después de llenar el dict .

Probablemente el caso de uso más común: agrupar elementos (en datos no ordenados, sino usar itertools.groupby )

# really verbose new = {} for (key, value) in data: if key in new: new[key].append( value ) else: new[key] = [value] # easy with setdefault new = {} for (key, value) in data: group = new.setdefault(key, []) # key might exist already group.append( value ) # even simpler with defaultdict new = defaultdict(list) for (key, value) in data: new[key].append( value ) # all keys have a default already

A veces querrás asegurarte de que existen claves específicas después de crear un dict. defaultdict no funciona en este caso, ya que solo crea claves en acceso explícito. Creo que usas algo HTTP-ish con muchos encabezados, algunos son opcionales, pero quieres los predeterminados para ellos:

headers = parse_headers( msg ) # parse the message, get a dict # now add all the optional headers for headername, defaultvalue in optional_headers: headers.setdefault( headername, defaultvalue )


Teóricamente hablando, setdefault aún sería útil si a veces desea establecer un valor predeterminado y otras no. En la vida real, no he encontrado ese caso de uso.

Sin embargo, un caso de uso interesante surge de la biblioteca estándar (Python 2.6, _threadinglocal.py):

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

Yo diría que usar __dict__.setdefault es un caso bastante útil.

Editar : sucede que este es el único ejemplo en la biblioteca estándar y está en un comentario. Entonces puede ser que no sea un caso suficiente para justificar la existencia de setdefault . Aún así, aquí hay una explicación:

Los objetos almacenan sus atributos en el atributo __dict__ . Como sucede, el atributo __dict__ es escribible en cualquier momento después de la creación del objeto. También es un diccionario, no un defaultdict . No es sensato que los objetos en el caso general tengan __dict__ como un defaultdict porque eso haría que cada objeto tenga todos los identificadores legales como atributos. Por lo tanto, no puedo prever ningún cambio en los objetos de Python __dict__.setdefault , además de eliminarlo por completo si no se considera útil.


Un caso de uso muy importante que acabo de tropezar: dict.setdefault() es ideal para código de subprocesos múltiples cuando solo quieres un único objeto canónico (a diferencia de varios objetos que pasan a ser iguales).

Por ejemplo, el (Int)Flag Enum en Python 3.6.0 tiene un error : si hay varios hilos compitiendo por un miembro compuesto (Int)Flag , puede terminar siendo más de uno:

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 solución es usar setdefault() como el último paso para guardar el miembro compuesto calculado: si ya se ha guardado otro, se usa en lugar del nuevo, lo que garantiza miembros exclusivos de Enum.


Un posible inconveniente de defaultdict over dict ( dict.setdefault ) es que un objeto defaultdict crea un nuevo elemento cada vez que se da una clave no existente (por ejemplo, con print , == ). También la clase defaultdict es mucho menos común que la clase dict (serialización, representación, etc.).

Las funciones PSO IMO (métodos) no destinados a mutar un objeto, no deberían mutar un objeto.


Uso setdefault con frecuencia cuando, obtengo esto, establezco un valor predeterminado (!!!) en un diccionario; algo común el diccionario os.environ:

# Set the venv dir if it isn''t already overridden: os.environ.setdefault(''VENV_DIR'', ''/my/default/path'')

De manera menos sucinta, esto se ve así:

# Set the venv dir if it isn''t already overridden: if ''VENV_DIR'' not in os.environ: os.environ[''VENV_DIR''] = ''/my/default/path'')

Vale la pena señalar que también puede usar la variable resultante:

venv_dir = os.environ.setdefault(''VENV_DIR'', ''/my/default/path'')

Pero eso es menos necesario de lo que era antes de que existieran los valores predeterminados.


Yo uso setdefault() cuando quiero un valor predeterminado en un OrderedDict . No hay una colección estándar de Python que haga ambas cosas, pero hay ways de implementar dicha colección.


[Editar] Muy mal! Setdefault siempre activaría long_computation, Python está ansioso.

Ampliando la respuesta de Tuttle. Para mí, el mejor caso de uso es el mecanismo de caché. En lugar de:

if x not in memo: memo[x]=long_computation(x) return memo[x]

que consume 3 líneas y 2 o 3 búsquedas, felizmente escribiría :

return memo.setdefault(x, long_computation(x))


defaultdict es excelente cuando el valor predeterminado es estático, como una nueva lista, pero no tanto si es dinámico.

Por ejemplo, necesito un diccionario para asignar cadenas a ints únicos. defaultdict(int) siempre usará 0 para el valor predeterminado. Del mismo modo, defaultdict(intGen()) siempre produce 1.

En cambio, usé un dict regular:

nextID = intGen() myDict = {} for lots of complicated stuff: #stuff that generates unpredictable, possibly already seen str strID = myDict.setdefault(myStr, nextID())

Tenga en cuenta que dict.get(key, nextID()) es insuficiente porque también necesito poder referirme a estos valores más adelante.

intGen es una pequeña clase I build que automáticamente incrementa un int y devuelve su valor:

class intGen: def __init__(self): self.i = 0 def __call__(self): self.i += 1 return self.i

Si alguien tiene una forma de hacer esto con un defaultdict , me encantaría verlo.