Python: converter defaultdict em dict

93

Como posso converter um defaultdict

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

para ser um ditado comum?

{'2': ['a'], '3': ['b'], '1': ['b', 'a']}
user2988464
fonte
11
dict(number_to_letter)
Tim Peters
2
@TimPeters uma vez que esta é uma pergunta bem escrita, com um título óbvio (e deve aparecer facilmente nas pesquisas), para referência futura, valeria a pena colocar isso como uma resposta se não conseguirmos encontrar um idiota que seja melhor. .
Jon Clements
Esta resposta deve ajudar: stackoverflow.com/a/3031915/1628832
karthikr
1
@JonClements, bom ponto, mas o DSM já o fez - espero que sua resposta seja aceita :-)
Tim Peters
2
@TimPeters, parabéns pelos 10k aliás ... Tive a sensação de que não demoraria muito - você deveria (plug sem-vergonha) aparecer em chat.stackoverflow.com/rooms/6/python em algum momento :)
Jon Clements

Respostas:

124

Você pode simplesmente ligar para dict:

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

mas lembre-se de que um defaultdict é um dict:

>>> isinstance(a, dict)
True

apenas com um comportamento ligeiramente diferente, em que quando você tenta acessar uma chave que está faltando - o que normalmente geraria um KeyError- o default_factoryé chamado em vez disso:

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

É isso que você vê quando print aaparece antes do lado dos dados do dicionário.

Portanto, outro truque para obter de volta um comportamento mais semelhante ao de um ditado, sem realmente fazer um novo objeto, é redefinir 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

mas na maioria das vezes isso não vale a pena.

DSM
fonte
Acabei de começar a aprender programação com Python. Você pode explicar mais sobre como usar default_factory? Eu simplesmente não tenho ideia do que está acontecendo. Desculpa.
user2988464
E o que é 'KeyError'?
user2988464
5
@ user2988464: você já usou default_factory- essa é a função que você passou quando fez o defaultdictem primeiro lugar, como defaultdict(int)ou defaultdict(list). Sempre que você tenta procurar uma chave no dicionário, como a['3'], por exemplo, em um dicionário comum, você obtém o valor ou gera uma KeyErrorexceção (como um erro, informando que não foi possível encontrar aquela chave). Mas em um defaultdict, ele chama a default_factoryfunção para criar um novo valor padrão (que é o "padrão" em defaultdict) para você.
DSM
3
É útil notar que a propriedade do template .items do Django não funciona com defaultdict, e é por isso que acabei aqui! Portanto, há um bom motivo para a conversão
Ben
2
Também é útil observar que pickle(para salvar objetos Python em um arquivo) não pode lidar com defaultdicts em muitas circunstâncias .
drevicko
28

Se o seu defaultdict for definido recursivamente, por exemplo:

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

ainda outra maneira simples de converter defaultdict de volta em dict é usar o jsonmódulo

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

e, claro, os valores contidos em seu defaultdict precisam ser confinados aos tipos de dados suportados por json, mas não deve ser um problema se você não tem a intenção de armazenar classes ou funções no dicionário.

Miau
fonte
9
Boa ideia, uma pena que meus dados não serão serializados em JSON: * (
Kasapo
15

Se você deseja uma versão recursiva para converter um recursivo defaultdictem um, dictpode tentar o seguinte:

#! /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
fonte
Isso é exatamente o que eu precisava! Infelizmente, meu defaultdict tem algumas chaves que não são serializáveis ​​em JSON sem um codificador personalizado (as chaves são tuplas - não é minha escolha, apenas meu problema)
Kasapo
1
Um bom. Usar a compreensão de um dicionário é sucinto se tiver apenas duas camadas de profundidade (ou seja defaultdict(lambda: defaultdict(int))): {k: dict(v) for k, v in foo.items()}
ggorlen