brian c
Digamos que tengo una clase de datos en python3. Quiero poder hacer hash y ordenar estos objetos.
Solo los quiero ordenados / hash en id.
Veo en los documentos que solo puedo implementar _picadillo_ y todo eso, pero me gustaría que datacalsses hicieran el trabajo por mí porque están destinados a manejar esto.
from dataclasses import dataclass, field
@dataclass(eq=True, order=True)
class Category:
id: str = field(compare=True)
name: str = field(default="set this in post_init", compare=False)
a = sorted(list(set([ Category(id='x'), Category(id='y')])))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'Category'
De los documentos:
Estas son las reglas que rigen la creación implícita de un
__hash__()
método:[…]
Si
eq
yfrozen
ambos son verdaderos, por defectodataclass()
generará un__hash__()
método para ti. Sieq
es cierto yfrozen
Es falso,__hash__()
se establecerá enNone
, marcándolo unhashable (que lo es, ya que es mutable). Sieq
Es falso,__hash__()
se dejará intacto, lo que significa que el__hash__()
Se usará el método de la superclase (si la superclase es un objeto, esto significa que recurrirá al hashing basado en id).
Desde que pusiste eq=True
E izquierda frozen
por defecto (False
), su clase de datos no se puede modificar.
Tienes 3 opciones:
- Establecer
frozen=True
(además deeq=True
), lo que hará que su clase sea inmutable y hashable. -
Establecer
unsafe_hash=True
que creará un__hash__
pero deje su clase mutable, lo que corre el riesgo de tener problemas si una instancia de su clase se modifica mientras está almacenada en un dictado o conjunto:cat = Category('foo', 'bar') categories = {cat} cat.id = 'baz' print(cat in categories) # False
- Implementar manualmente un
__hash__
método.
-
Como se indica a continuación, para excluir algún campo del uso para picadillo generación en unsafe_hash, puede usar field(compare=False) o field(hash=False) (el hash hereda el valor de comparación si no está configurado).
– Leo Ufimtsev
29 de julio de 2019 a las 3:18
-
Tenga en cuenta que implementar manualmente
__hash__()
es trivial en el caso de un campo de tipo ID:def __hash__(self): return hash(self.id)
– señora
3 de enero de 2020 a las 21:33
Me gustaría agregar una nota especial para el uso de unsafe_hash.
Puede excluir campos de la comparación mediante hash configurando compare=False o hash=False. (hash por defecto hereda de comparar).
Esto podría ser útil si almacena nodos en un gráfico pero quiere marcarlos como visitados sin romper su hashing (por ejemplo, si están en un conjunto de nodos no visitados…).
from dataclasses import dataclass, field
@dataclass(unsafe_hash=True)
class node:
x:int
visit_count: int = field(default=10, compare=False) # hash inherits compare setting. So valid.
# visit_count: int = field(default=False, hash=False) # also valid. Arguably easier to read, but can break some compare code.
# visit_count: int = False # if mutated, hashing breaks. (3* printed)
s = set()
n = node(1)
s.add(n)
if n in s: print("1* n in s")
n.visit_count = 11
if n in s:
print("2* n still in s")
else:
print("3* n is lost to the void because hashing broke.")
esto me llevó horas para averiguarlo… Lecturas adicionales útiles que encontré es el documento de python sobre clases de datos. Consulte específicamente la documentación de campo y la documentación de argumento de clase de datos.
https://docs.python.org/3/library/dataclasses.html
-
Esto podría ser útil si almacena nodos en un gráfico pero quiere marcarlos como visitados sin romper su hashing (por ejemplo, si están en un conjunto de nodos no visitados…).: nunca me había sentido más dirigido por un caso de uso de ejemplo.
– Álex Povel
13 de diciembre de 2021 a las 21:43
Espacio profundo
TL;DR
Usar frozen=True
en conjunto con eq=True
(lo que hará que las instancias sean inmutables).
Respuesta larga
Desde el documentos:
__hash__()
es utilizado por incorporadohash()
y cuando se agregan objetos a colecciones con hash, como diccionarios y conjuntos. Teniendo un__hash__()
implica que las instancias de la clase son inmutables. La mutabilidad es una propiedad complicada que depende de la intención del programador, la existencia y el comportamiento de__eq__()
y los valores de las banderas eq y frozen en eldataclass()
decorador.Por defecto,
dataclass()
no agregará implícitamente un__hash__()
método a menos que sea seguro hacerlo. Tampoco agregará o cambiará un existente explícitamente definido__hash__()
método. Establecer el atributo de clase
__hash__ = None
tiene un significado específico para Python, como se describe en el__hash__()
documentación.Si
__hash__()
no está definido explícitamente, o si está configurado en Ninguno, entonces
dataclass()
puede agregar un implícito__hash__()
método. Aunque no se recomienda, puede forzardataclass()
para crear un__hash__()
método conunsafe_hash=True
. Este podría ser el caso si su clase es lógicamente inmutable pero, no obstante, se puede mutar. Este es un caso de uso especializado y debe ser considerado cuidadosamente.Estas son las reglas que rigen la creación implícita de un
__hash__()
método. Tenga en cuenta que ambos no pueden tener un explícito__hash__()
método en su clase de datos y establecerunsafe_hash=True
; esto resultará en unTypeError
.Si eq y frozen son verdaderos, por defecto
dataclass()
generará un
__hash__()
método para ti. Si eq es verdadera y frozen es falsa,__hash__()
se establecerá en Ninguno, marcándolo como no modificable (que lo es, ya que es mutable). Si eq es falsa,__hash__()
se dejará intacto, lo que significa que el__hash__()
Se usará el método de la superclase (si la superclase es un objeto, esto significa que recurrirá al hashing basado en id).
Para encontrar ejemplos, consulte el Lo que puedes encender sección en esta publicación stackoverflow.com/a/52283085/4531270.
– pilang
30/11/2018 a las 20:55