Múltiples asignaciones en un diccionario de python

4 minutos de lectura

¿Es posible asignar valores a más de una tecla de un diccionario de una manera más concisa que la de abajo?

quiero decir, deja d ser un diccionario inicializado de la siguiente manera:

d={'a':1,'b':2,'c':3}

Para asignar valores a varias claves, necesito hacer esto:

d['a']=10
d['b']=200
d['c']=30

¿Puedo lograr lo mismo con algo como esto:

d['a','b','c']=10,200,30

Gracias.

Avatar de usuario de Aamir Rind
Aamir Corteza

Puedes usar dict.update:

d.update({'a': 10, 'c': 200, 'c': 30})

Esto sobrescribirá los valores de las claves existentes y agregará nuevos pares clave-valor para las claves que aún no existen.

  • Sí, esto seguro que funciona. yo sabía update()pero gracias.

    – ozi

    19 de enero de 2014 a las 21:09

  • También puedes escribir esto como d.update(a=10, b=200, c=300)siempre que las claves sean identificadores válidos (al igual que en el constructor).

    – abarnert

    20 de enero de 2014 a las 0:20


  • Hay una manera más rápida: d = {**d, **dict(zip(keys, values))}. Vea mi comentario para más detalles: stackoverflow.com/a/45043651/618045

    – Sklavit

    13/07/2017 a las 17:40

  • @WiseStrawberry. ¿Por qué? Esta es la sintaxis legal de python (Python >=3.5).

    – Sklavit

    23 de agosto de 2017 a las 14:51


  • No digo que no sea una sintaxis de python legal, simplemente no es muy legible, creo que es elegante, pero no legible. Sólo mis 2 centavos. En Python, el código legible se prefiere en general sobre el código complejo.

    – Fresa Sabia

    24 de agosto de 2017 a las 11:01

Avatar de usuario de Delgan
Delgan

También puede simplemente usar la semántica de asignación múltiple:

d['a'], d['b'], d['c'] = 10, 200, 30

  • solución más elegante

    – kay

    26 de marzo de 2020 a las 16:17

Siempre puedes envolverlo en una función:

def multiassign(d, keys, values):
    d.update(zip(keys, values))

Incluso si no sabías sobre updatepodrías escribirlo así:

def multiassign(d, keys, values):
    for k, v in zip(keys, values):
        d[k] = v

O incluso puedes escribir un dict subclase que le da exactamente la sintaxis que quería:

class EasyDict(dict):
    def __getitem__(self, key):
        if isinstance(key, tuple):
            return [super().__getitem__(k) for k in key]
        else:
            return super().__getitem__(key)
    def __setitem__(self, key, value):
        if isinstance(key, tuple):
            self.update(zip(key, value))
        else:
            super().__setitem__(key, value)
    def __delitem__(self, key, value):
        if isinstance(key, tuple):
            for k in key: super().__delitem__(k)
        else:
            super().__setitem__(key, value)

Ahora:

>>> d = {'a': 1, 'd': 4}
>>> multiassign(d, ['a', 'b', 'c'], [10, 200, 300])
>>> d
{'a': 10, 'b': 200, 'c': 300, 'd': 4}
>>> d2 = EasyDict({'a': 1, 'd': 4})
>>> d2['a', 'b', 'c'] = 100, 200, 300
>>> d2
{'a': 10, 'b': 200, 'c': 300, 'd': 4}

Solo tenga en cuenta que obviamente ya no será posible usar tuplas como claves en un EasyDict.

Además, si va a usar esto para algo serio, probablemente quiera mejorar el manejo de errores. (d['a', 'b'] = 1 dará un mensaje críptico sobre zip argument #2 must support iteration, d['a', 'b', 'c'] = 1, 2 trabajará en silencio y no hará nada para cetc.)

Una comparación de velocidad, de peor a mejor:

Python 3.5.3 |Continuum Analytics, Inc.| (default, May 15 2017, 10:43:23) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy.random as nprnd
   ...: d = dict([(_, nprnd.rand()) for _ in range(1000)])
   ...: values = nprnd.randint(1000, size=10000)
   ...: keys = nprnd.randint(1000, size=10000)
   ...: def multiassign(d, keys, values):
   ...:     for k, v in zip(keys, values):
   ...:         d[k] = v
   ...:
   ...: d1 = dict(d)
   ...: %timeit multiassign(d1, keys, values)
   ...: d1 = dict(d)
   ...: %timeit {**d1, **{keys[i]: values[i] for i in range(len(keys))}}
   ...: d1 = dict(d)
   ...: %timeit d1.update(zip(keys, values))
   ...: d1 = dict(d)
   ...: %timeit {*d1.items(), *zip(keys, values)}
   ...: d1 = dict(d)
   ...: %timeit {**d1, **{key: value for key, value in zip(keys, values)}}
   ...: d1 = dict(d)
   ...: %timeit {**d1, **dict(zip(keys, values))}
4 ms ± 25.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.66 ms ± 29.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.17 ms ± 31.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.81 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.38 ms ± 75.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.96 ms ± 21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Entonces, el claro ganador es la recreación del diccionario de los diccionarios.

Podrías usar un diccionario de comprensión, por ejemplo

letters, nos = ['a','b','c'], [1,2,3]
d = {letters[i]: nos[i] for i in range(len(nos))}

producción:

{‘a’: 1, ‘c’: 3, ‘b’: 2}

  • Con datos en ese formato, dict(zip(letters, nos)) sería más ordenado

    – jonrsharpe

    19 de enero de 2014 a las 22:08

  • Con datos en ese formato, dict(zip(letters, nos)) sería más ordenado

    – jonrsharpe

    19 de enero de 2014 a las 22:08

¿Ha sido útil esta solución?