当前位置: 首页 > 知识库问答 >
问题:

如何使python数据类可哈希?

唐高朗
2023-03-14

说a我在python3中有一个数据类。我希望能够散列和排序这些对象。

我只想在id上订购/散列它们。

我在文档中看到,我可以只实现_hash_和所有这些,但我想让数据类为我做这些工作,因为它们旨在处理这些。

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'

共有3个答案

叶嘉颖
2023-03-14

TL;博士

将< code>frozen=True与< code>eq=True结合使用(这将使实例不可变)。

长回答

从文档中:

__hash__() 由内置 hash() 使用,并且当对象添加到哈希集合(如字典和集)时使用。具有__hash__()意味着该类的实例是不可变的。可变性是一个复杂的属性,取决于程序员的意图、__eq__() 的存在和行为,以及 dataclass() 装饰器中 eq 和冻结标志的值。

默认情况下,dataclass()不会隐式添加__hash__()方法,除非这样做是安全的。它也不会添加或更改现有的显式定义的__hash__()方法。设置类属性__hash__=无对Python有特定的含义,如__hash代码>留档中所述。

如果__hash__()未显式定义,或设置为None,则dataclass()可能会添加隐式 方法。尽管不推荐,但您可以强制 dataclass()创建一个 __hash_()方法,其中 。如果您的类在逻辑上是不可变的,但仍然可以修改,则可能会出现这种情况。这是一个特殊的用例,应该仔细考虑。

以下是管理< code>__hash__()方法的隐式创建的规则。请注意,您不能在dataclass中既有显式的< code>__hash__()方法,又设置< code > unsafe _ hash = True ;这将导致一个< code>TypeError。

如果eq和freezed都为true,默认情况下dataclass()将为您生成一个__hash_()方法。如果eq为true,freezed为false,__hash__()将设置为None,将其标记为不可损坏(因为它是可变的)。如果eq为false,__hash__()将保持不变,这意味着将使用超类的__hash()方法(如果超类是object,则意味着它将回退到基于id的哈希)。

江礼骞
2023-03-14

我想加一个unsafe_hash使用的特别说明。

通过设置compare=False或hash=False,可以将字段排除在哈希比较之外。(默认情况下,哈希继承自compare)。

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

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

欧阳正德
2023-03-14

从文档中:

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

[...]

如果 eq冻结都为真,则默认情况下,dataclass() 将为您生成一个 __hash__() 方法。如果 eq 为 true 且冻结为假,则 __hash__() 将设置为 None,将其标记为不可哈希(确实如此,因为它是可变的)。如果 eq 为 false,则__hash__() 将保持不变,这意味着将使用超类的 __hash__() 方法(如果超类是对象,这意味着它将回退到基于 id 的哈希)。

由于您设置了 eq=True 并保持冻结状态,因此您的数据类是不可哈希的。

您有3个选择:

>

  • 设置冻结=True(除了eq=True),这将使您的类不可变且可散列。
  • Setunsafe_hash=True,这将创建一个__hash__方法,但使您的类保持可变,因此如果您的类的实例在存储在dicor set中时被修改,则可能会出现问题:

    cat = Category('foo', 'bar')
    categories = {cat}
    cat.id = 'baz'
    
    print(cat in categories)  # False
    

  •  类似资料:
    • 问题内容: 在python中使用户定义的类可排序和/或可哈希化时,需要重写/实现哪些方法? 需要注意的陷阱是什么? 我输入解释器以获取内置字典的方法列表。其中,我假设我需要一些实现 与Python2相比,必须为Python3实现哪些方法有区别吗? 问题答案: 我差点把它作为对其他答案的评论,但这本身就是一个答案。 为了使您的项目可排序,它们只需要实现。这是内置排序使用的唯一方法。 其他比较还是仅在

    • 问题内容: Java的访问级别表显示了用于控制对类成员的访问的4个不同选项: 但是,没有用于“仅类和子类可访问”的修饰符。那是: 是否有可能在Java中定义这种访问级别? 如果是这样,怎么办? 如果无法做到这一点,那一定是由于经过深思熟虑的设计原则。如果是这样,那是什么原则。换句话说,为什么在Java中具有这样的访问级别不是一个好主意? 问题答案: 设计原则是开发人员不要恶意入侵自己的程序。如果您

    • 问题内容: 如何使类可序列化? 一个简单的类: 我应该怎么做才能获得以下输出: 问题答案: 你对预期的输出有想法吗?例如这样做吗? 在这种情况下,你只能致电。 如果你想要更多的自定义输出,则必须继承并实现自己的自定义序列化。 有关一个简单的示例,请参见下文。 然后,将该类作为 传递给方法: 如果你也想解码,那么你将有一个自定义供应类。例如

    • 问题内容: 这是我的代码: 为什么会这样呢? 第一个和第三个对象具有相同的内容和相同的哈希值,但它们讲述了3个唯一的对象? 问题答案: 您还需要以兼容的方式进行定义–否则,相等性将基于对象身份。 在Python 2上,建议您还定义与保持一致。在Python 3上,默认实现将为您委托。

    • 问题内容: 如何使Python类可序列化? 一个简单的类: 我应该怎么做才能获得以下输出: 没有错误 问题答案: 你对预期的输出有想法吗?例如这样做吗? 在这种情况下,你只能致电。 如果你想要更多的自定义输出,则必须继承并实现自己的自定义序列化。 有关一个简单的示例,请参见下文。 然后,将该类作为传递给方法: 如果你也想解码,那么你将有一个自定义供应给类。例如

    • 想给方法自定义数据类型,通过资料查到可以用 dataclass 这样就很简洁,但貌似传入必须是实例化的方式,我更希望是字典的形式,请问有什么简洁的好的方法?