我如何才能使一个 python 数据类散列?

假设我在 python3中有一个数据类,我希望能够对这些对象进行散列和排序。

我只希望它们在 ID 上有序/散列。

I see in the docs that I can just implement _大麻_ and all that but I'd like to get datacalsses to do the work for me because they are intended to handle this.

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'
47568 次浏览

TL;DR

frozen=Trueeq=True结合使用(这将使实例不可变)。

长答案

来自 医生:

__hash__()由内置的 hash()使用,当对象被添加到散列集合(如字典和集合)时使用。血氧饱和度正常 意味着类的实例是不可变的 复杂的属性,这取决于程序员的意图 existence and behavior of __eq__(), and the values of the eq and 在 dataclass()室内设计师中冻结的旗帜。

默认情况下,dataclass()不会隐式添加 __hash__()方法 除非这样做是安全的。它也不会添加或更改现有的 显式定义的 __hash__()方法。设置 class 属性 如 __hash__() 文档所述,__hash__ = None对 Python 具有特定的意义。

如果没有显式定义 __hash__(),或者将其设置为 Nothing,则 dataclass()可以添加隐式 __hash__()方法 建议,可以强制 dataclass()创建 __hash__()方法 如果你的类是 逻辑上不可变,但仍然可以变异。这是一个 specialized use case and should be considered carefully.

下面是控制隐式创建 __hash__()方法的规则。 注意,不能同时具有显式的 __hash__()方法 类并设置 unsafe_hash=True; 这将导致 TypeError

如果 eq 和 zen 都为 true,则默认情况下 dataclass()将生成一个 __hash__()方法。如果 eq 为 true,则冻结为 false,则 __hash__()将被设置为 Nothing,并将其标记为 unhash (因为它是可变的,所以它是 unhash)。如果 eq 为 false,则留下 __hash__() 这意味着将使用超类的 __hash__()方法 (如果超类是 object,这意味着它将回到基于 id 的 哈希)。

From 那些文件:

以下是隐式创建 __hash__()方法的规则:

[...]

如果 eqfrozen都为真,则默认情况下 dataclass()为 为您生成一个 __hash__()方法。如果 eq为真,则 frozen 如果为 false,则 __hash__()将设置为 None,标记为不可散列 (因为它是可变的)。如果 eq为 false,则 __hash__()为 将保持不变,这意味着 __hash__()方法的 超类将被使用(如果超类是对象,这意味着它 将回到基于 id 的散列)。

Since you set eq=True and left frozen at the default (False), your dataclass is unhashable.

你有三个选择:

  • 设置 frozen=True(除了 eq=True) ,这将使您的类不可变和散列。
  • 设置 unsafe_hash=True,它将创建一个 __hash__方法,但是让你的类可变,因此,如果你的类的一个实例被修改,而存储在一个 dict 或 Set:

    cat = Category('foo', 'bar')
    categories = {cat}
    cat.id = 'baz'
    
    
    print(cat in categories)  # False
    
  • Manually implement a __hash__ method.

I'd like to add a special note for use of unsafe_hash.

可以通过设置 than = False 或 hash = False 来排除通过散列进行比较的字段。(默认情况下哈希从比较中继承)。

如果您将节点存储在一个图中,但希望在不破坏散列的情况下标记访问的节点(例如,如果它们位于一组未访问的节点中) ,那么这可能非常有用。.).

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.")


这花了我的 几个小时找出... 有用的进一步读取我发现是在数据类的 python 文档。具体请参见字段文档和数据类 arg 文档。 Https://docs.python.org/3/library/dataclasses.html