Python: convierte defaultdict en dict

90

¿Cómo puedo convertir un defaultdict

number_to_letter
defaultdict(<class 'list'>, {'2': ['a'], '3': ['b'], '1': ['b', 'a']})

ser un dictado común?

{'2': ['a'], '3': ['b'], '1': ['b', 'a']}
usuario2988464
fuente
10
dict(number_to_letter)
Tim Peters
2
@TimPeters ya que esta es en realidad una pregunta muy bien escrita, con un título obvio (y debería aparecer fácilmente en las búsquedas), para referencia futura, valdría la pena poner eso como respuesta si no podemos encontrar un engañado que sea mejor ... .
Jon Clements
Esta respuesta debería ayudar: stackoverflow.com/a/3031915/1628832
karthikr
1
@JonClements, buen punto, pero DSM ya lo hizo, espero que su respuesta sea aceptada :-)
Tim Peters
2
@TimPeters felicidades por los 10k por cierto ... Tenía la sensación de que no te llevaría mucho tiempo; deberías (enchufe descarado) aparecer en chat.stackoverflow.com/rooms/6/python en algún momento :)
Jon Clements

Respuestas:

121

Simplemente puede llamar dict:

>>> a
defaultdict(<type 'list'>, {'1': ['b', 'a'], '3': ['b'], '2': ['a']})
>>> dict(a)
{'1': ['b', 'a'], '3': ['b'], '2': ['a']}

pero recuerde que un defaultdict es un dict:

>>> isinstance(a, dict)
True

solo con un comportamiento ligeramente diferente, ya que cuando intenta acceder a una clave que falta, que normalmente generaría un KeyError, default_factoryse llama en su lugar:

>>> a.default_factory
<type 'list'>

Eso es lo que ve cuando print aaparece antes el lado de datos del diccionario.

Entonces, otro truco para recuperar un comportamiento más parecido a un dictado sin realmente crear un nuevo objeto es restablecer default_factory:

>>> a.default_factory = None
>>> a[4].append(10)
Traceback (most recent call last):
  File "<ipython-input-6-0721ca19bee1>", line 1, in <module>
    a[4].append(10)
KeyError: 4

pero la mayoría de las veces esto no vale la pena.

DSM
fuente
Acabo de empezar a aprender a programar con Python. ¿Puede explicar más sobre cómo usarlo default_factory? Simplemente no tengo ni idea de lo que está pasando. Lo siento.
user2988464
¿Y qué es 'KeyError'?
user2988464
4
@ user2988464: ya usaste default_factory; esa es la función que pasaste cuando hiciste el defaultdicten primer lugar, como defaultdict(int)o defaultdict(list). Cada vez que intenta buscar una clave en el diccionario, como a['3'], por ejemplo, en un diccionario habitual, obtiene el valor o genera una KeyErrorexcepción (como un error, que le dice que no pudo encontrar esa clave). Pero en a defaultdict, en cambio llama a la default_factoryfunción para crear un nuevo valor predeterminado (que es el "predeterminado" en defaultdict) para usted.
DSM
2
Es útil notar que la propiedad de plantilla .items de Django no funcionará con defaultdict, ¡por eso terminé aquí! Así que hay una buena razón para la conversión
Ben
2
También es útil tener en cuenta que pickle(para guardar objetos de Python en un archivo) no se pueden manejar correos defaultdictelectrónicos en muchas circunstancias .
drevicko
28

Si su defaultdict se define de forma recursiva, por ejemplo:

from collections import defaultdict
recurddict = lambda: defaultdict(recurddict)
data = recurddict()
data["hello"] = "world"
data["good"]["day"] = True

otra forma sencilla de convertir defaultdict de nuevo a dict es usar el jsonmódulo

import json
data = json.loads(json.dumps(data))

y, por supuesto, los valores contenidos en su defaultdict deben limitarse a los tipos de datos compatibles con json, pero no debería ser un problema si no intenta almacenar clases o funciones en el dict.

maullar
fuente
8
Buena idea, lástima que mis datos no se serializarán en JSON: * (
Kasapo
15

Si incluso desea una versión recursiva para convertir un recursivo defaultdicten uno dict, puede probar lo siguiente:

#! /usr/bin/env python3

from collections import defaultdict

def ddict():
    return defaultdict(ddict)

def ddict2dict(d):
    for k, v in d.items():
        if isinstance(v, dict):
            d[k] = ddict2dict(v)
    return dict(d)

myddict = ddict()

myddict["a"]["b"]["c"] = "value"

print(myddict)

mydict = ddict2dict(myddict)

print(mydict)
white_gecko
fuente
¡Eso es exactamente lo que necesitaba! Desafortunadamente, mi defaultdict tiene algunas claves que no son serializables JSON sin un codificador personalizado (las claves son tuplas, no es mi elección, solo mi problema)
Kasapo
Buena. Usar una comprensión de diccionario es sucinto si solo tiene dos capas de profundidad (es decir defaultdict(lambda: defaultdict(int))): {k: dict(v) for k, v in foo.items()}
ggorlen