O Python não possui um tipo frozendict interno. Acontece que isso não seria útil com muita frequência (embora provavelmente ainda seja útil com mais frequência do que frozenset
é).
O motivo mais comum para querer esse tipo é quando a função de memorização solicita funções com argumentos desconhecidos. A solução mais comum para armazenar um equivalente lavável de um ditado (onde os valores são laváveis) é algo parecido tuple(sorted(kwargs.iteritems()))
.
Isso depende da classificação não ser um pouco insana. O Python não pode prometer positivamente que a classificação resultará em algo razoável aqui. (Mas não pode prometer muito mais, por isso não se preocupe muito.)
Você poderia facilmente criar algum tipo de invólucro que funciona como um ditado. Pode parecer algo como
import collections
class FrozenDict(collections.Mapping):
"""Don't forget the docstrings!!"""
def __init__(self, *args, **kwargs):
self._d = dict(*args, **kwargs)
self._hash = None
def __iter__(self):
return iter(self._d)
def __len__(self):
return len(self._d)
def __getitem__(self, key):
return self._d[key]
def __hash__(self):
# It would have been simpler and maybe more obvious to
# use hash(tuple(sorted(self._d.iteritems()))) from this discussion
# so far, but this solution is O(n). I don't know what kind of
# n we are going to run into, but sometimes it's hard to resist the
# urge to optimize when it will gain improved algorithmic performance.
if self._hash is None:
hash_ = 0
for pair in self.items():
hash_ ^= hash(pair)
self._hash = hash_
return self._hash
Deve funcionar muito bem:
>>> x = FrozenDict(a=1, b=2)
>>> y = FrozenDict(a=1, b=2)
>>> x is y
False
>>> x == y
True
>>> x == {'a': 1, 'b': 2}
True
>>> d = {x: 'foo'}
>>> d[y]
'foo'
__hash__
método pode ser ligeiramente aprimorado. Basta usar uma variável temporária ao calcular o hash e definir apenasself._hash
quando você tiver o valor final. Dessa forma, outro encadeamento que obtém um hash enquanto o primeiro está calculando fará simplesmente um cálculo redundante, em vez de obter um valor incorreto.Curiosamente, apesar de raramente termos utilidade
frozenset
em python, ainda não existe mapeamento congelado. A idéia foi rejeitada no PEP 416 - Adicione um tipo embutido no frozendict . A idéia pode ser revisitada no Python 3.9, consulte PEP 603 - Adicionando um tipo de mapa congelado às coleções .Portanto, a solução python 2 para isso:
Ainda parece ser o pouco coxo:
Em python3 você tem a opção disso :
Agora a configuração padrão pode ser atualizada dinamicamente, mas permanece imutável onde você deseja que seja imutável, passando pelo proxy.
Portanto, as alterações na
default_config
atualização serãoDEFAULTS
esperadas, mas você não pode gravar no próprio objeto de proxy de mapeamento.É certo que não é a mesma coisa que um "ditado imutável e lavável" - mas é um substituto decente, dado o mesmo tipo de casos de uso para os quais podemos querer um frozendict.
fonte
def foo(config=MappingProxyType({'a': 1})):
? Seu exemplo ainda permite modificações globaisdefault_config
também.config = default_config = {'a': 1}
seja um erro de digitação.Supondo que as chaves e os valores do dicionário sejam imutáveis (por exemplo, strings), então:
fonte
dict(t)
Não existe
fronzedict
, mas você pode usar oMappingProxyType
que foi adicionado à biblioteca padrão com o Python 3.3:fonte
TypeError: can't pickle mappingproxy objects
Aqui está o código que eu tenho usado. Eu subclassifiquei frozenset. As vantagens disso são as seguintes.
Atualização em 21 de janeiro de 2015: o código original que publiquei em 2014 usou um loop for para encontrar uma chave que correspondesse. Isso foi incrivelmente lento. Agora eu montei uma implementação que tira proveito dos recursos de hash do frozenset. Os pares de valores-chave são armazenados em contêineres especiais, onde as funções
__hash__
e__eq__
são baseadas apenas na chave. Este código também foi formalmente testado em unidade, ao contrário do que publiquei aqui em agosto de 2014.Licença estilo MIT.
fonte
Item
para ser o hash da chave é um puro truque!diff(diff({key}))
ainda é linear no tamanho do FrozenDict, enquanto o tempo de acesso regular ao dict é constante no caso médio.Penso em frozendict toda vez que escrevo uma função como esta:
fonte
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Você pode usar
frozendict
doutilspie
pacote como:Conforme o documento :
fonte
Instale o frozendict
Use-o!
fonte
Sim, esta é a minha segunda resposta, mas é uma abordagem completamente diferente. A primeira implementação foi em python puro. Este está em Cython. Se você sabe como usar e compilar módulos Cython, isso é tão rápido quanto um dicionário comum. Aproximadamente 0,04 a 0,06 microsseg para recuperar um único valor.
Este é o arquivo "frozen_dict.pyx"
Aqui está o arquivo "setup.py"
Se você tiver o Cython instalado, salve os dois arquivos acima no mesmo diretório. Mover para esse diretório na linha de comando.
E você deveria terminar.
fonte
A principal desvantagem
namedtuple
é que ele precisa ser especificado antes de ser usado, portanto, é menos conveniente para casos de uso único.No entanto, existe uma solução prática que pode ser usada para lidar com muitos desses casos. Digamos que você queira ter um equivalente imutável do seguinte ditado:
Isso pode ser emulado assim:
É até possível escrever uma função auxiliar para automatizar isso:
É claro que isso funciona apenas para ditados planos, mas não deve ser muito difícil implementar uma versão recursiva.
fonte
getattr(fa, x)
vez defa[x]
, nenhumkeys
método na ponta dos dedos e todos os outros motivos pelos quais um mapeamento pode ser desejável.Subclassificação
dict
Eu vejo esse padrão em estado selvagem (github) e queria mencioná-lo:
exemplo de uso:
Prós
get()
,keys()
,items()
(iteritems()
em PY2) e todas as guloseimas dedict
fora da caixa, sem implementá-las explicitamentedict
que significa desempenho (dict
é escrito em c no CPython)isinstance(my_frozen_dict, dict)
retorna True - embora o python incentive a digitação por patos de muitos pacotesisinstance()
, isso pode salvar muitos ajustes e personalizaçõesContras
__hash__
acelerar um pouco.fonte
__setitem__
e herdardict
é incrivelmente rápido em comparação com muitas alternativas.Outra opção é a
MultiDictProxy
classe domultidict
pacote.fonte
Eu precisava acessar chaves fixas para algo em um ponto para algo que era um tipo de coisa globalmente constante e decidi algo assim:
Use-o como
AVISO: Eu não recomendo isso para a maioria dos casos de uso, pois isso gera algumas trocas bastante graves.
fonte
Na ausência de suporte ao idioma nativo, você pode fazer isso sozinho ou usar uma solução existente. Felizmente, o Python simplifica bastante a extensão de suas implementações básicas.
fonte