Python no tiene un tipo de congelador incorporado. Resulta que esto no sería útil con demasiada frecuencia (aunque probablemente aún sería útil con más frecuencia de lo que frozenset
es).
La razón más común para querer ese tipo es cuando se memorizan llamadas de funciones para funciones con argumentos desconocidos. La solución más común para almacenar un equivalente hashable de un dict (donde los valores son hashaable) es algo así tuple(sorted(kwargs.iteritems()))
.
Esto depende de que la clasificación no sea un poco loca. Python no puede prometer positivamente que la clasificación dará como resultado algo razonable aquí. (Pero no puede prometer mucho más, así que no te preocupes demasiado).
Fácilmente podría hacer algún tipo de envoltura que funcione como un dict. Puede parecer algo así
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
Debería funcionar muy bien:
>>> 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 podría mejorarse ligeramente. Simplemente use una variable temporal al calcular el hash, y solo establezcaself._hash
una vez que tenga el valor final. De esa manera, otro hilo que obtiene un hash mientras el primero está calculando simplemente hará un cálculo redundante, en lugar de obtener un valor incorrecto.Curiosamente, aunque rara vez somos útiles
frozenset
en Python, todavía no hay mapeo congelado. La idea fue rechazada en PEP 416: agregue un tipo incorporado de congelación . La idea puede revisarse en Python 3.9, ver PEP 603 - Agregar un tipo de mapa congelado a las colecciones .Entonces, la solución de Python 2 para esto:
Todavía parece ser algo cojo:
En python3 tienes la opción de esto :
Ahora la configuración predeterminada se puede actualizar dinámicamente, pero permanece inmutable donde desea que sea inmutable pasando el proxy en su lugar.
Por lo tanto, los cambios en el
default_config
se actualizaránDEFAULTS
como se esperaba, pero no puede escribir en el objeto proxy de mapeo.Es cierto que no es exactamente lo mismo que un "dict inmutable y hashable", pero es un sustituto decente dado el mismo tipo de casos de uso para los que podríamos querer un congelado.
fuente
def foo(config=MappingProxyType({'a': 1})):
? Su ejemplo todavía permite la modificación globaldefault_config
también.config = default_config = {'a': 1}
es un error tipográfico.Suponiendo que las claves y los valores del diccionario son inmutables (por ejemplo, cadenas), entonces:
fuente
dict(t)
No existe
fronzedict
, pero puede usar elMappingProxyType
que se agregó a la biblioteca estándar con Python 3.3:fuente
TypeError: can't pickle mappingproxy objects
Aquí está el código que he estado usando. Subclase el grupo congelado. Las ventajas de esto son las siguientes.
Actualización del 21 de enero de 2015: el código original que publiqué en 2014 utilizó un bucle for para encontrar una clave que coincidiera. Eso fue increíblemente lento. Ahora he creado una implementación que aprovecha las funciones de hash de frozenset. Los pares clave-valor se almacenan en contenedores especiales donde las funciones
__hash__
y__eq__
se basan solo en la clave. Este código también se ha probado formalmente en unidades, a diferencia de lo que publiqué aquí en agosto de 2014.Licencia de estilo MIT.
fuente
Item
ser el hash de la clave es un buen truco!diff(diff({key}))
sigue siendo lineal en el tamaño de FrozenDict, mientras que el tiempo de acceso de dict regular es constante en el caso promedio.Pienso en frozendict cada vez que escribo una función como esta:
fuente
optional_dict_parm = optional_dict_parm or {}
types.MappingProxyType
({})
Es posible utilizar
frozendict
deutilspie
paquete como:Según el documento :
fuente
Instalar frozendict
Úsalo!
fuente
Sí, esta es mi segunda respuesta, pero es un enfoque completamente diferente. La primera implementación fue en Python puro. Este está en Cython. Si sabe cómo usar y compilar módulos de Cython, esto es tan rápido como un diccionario normal. Aproximadamente .04 a .06 microsegundos para recuperar un solo valor.
Este es el archivo "frozen_dict.pyx"
Aquí está el archivo "setup.py"
Si tiene instalado Cython, guarde los dos archivos anteriores en el mismo directorio. Mover a ese directorio en la línea de comando.
Y deberías haber terminado.
fuente
La principal desventaja
namedtuple
es que debe especificarse antes de usarse, por lo que es menos conveniente para casos de un solo uso.Sin embargo, existe una solución práctica que se puede utilizar para manejar muchos de estos casos. Digamos que desea tener un equivalente inmutable del siguiente dict:
Esto se puede emular así:
Incluso es posible escribir una función auxiliar para automatizar esto:
Por supuesto, esto funciona solo para dictos planos, pero no debería ser demasiado difícil implementar una versión recursiva.
fuente
getattr(fa, x)
lugar defa[x]
, ningúnkeys
método al alcance de la mano, y todas las otras razones por las que un mapeo puede ser deseable.Subclases
dict
Veo este patrón en la naturaleza (github) y quería mencionarlo:
ejemplo de uso:
Pros
get()
,keys()
,items()
(iteritems()
en la AP2) y todas las golosinas dedict
sacarlo de la caja sin implementar explícitamentedict
que significa rendimiento (dict
está escrito en c en CPython)isinstance(my_frozen_dict, dict)
devuelve True, aunque Python fomenta utilización de muchos tipos de paquetesisinstance()
, esto puede ahorrar muchos ajustes y personalizacionesContras
__hash__
un poco más rápido.fuente
__setitem__
y heredardict
es increíblemente rápido en comparación con muchas alternativas.Otra opción es la
MultiDictProxy
clase delmultidict
paquete.fuente
Necesitaba acceder a claves fijas para algo en un punto para algo que era una especie de cosa globalmente constante y me decidí por algo como esto:
Úselo como
ADVERTENCIA: No recomiendo esto para la mayoría de los casos de uso, ya que hace algunas compensaciones bastante graves.
fuente
En ausencia de soporte de idioma nativo, puede hacerlo usted mismo o usar una solución existente. Afortunadamente, Python hace que sea muy sencillo extender sus implementaciones base.
fuente