@dataclass(frozen=True)示例(Python 3.7)
@dataclass之前在https://stackoverflow.com/a/69313714/895245上提到过,但这里有一个例子。
这个很棒的新特性,除其他优点外,自动为你定义__hash__和__eq__方法,使其在dicts和set中正常工作:
dataclass_cheat.py
from dataclasses import dataclass, FrozenInstanceError
@dataclass(frozen=True)
class MyClass1:
n: int
s: str
@dataclass(frozen=True)
class MyClass2:
n: int
my_class_1: MyClass1
d = {}
d[MyClass1(n=1, s='a')] = 1
d[MyClass1(n=2, s='a')] = 2
d[MyClass1(n=2, s='b')] = 3
d[MyClass2(n=1, my_class_1=MyClass1(n=1, s='a'))] = 4
d[MyClass2(n=2, my_class_1=MyClass1(n=1, s='a'))] = 5
d[MyClass2(n=2, my_class_1=MyClass1(n=2, s='a'))] = 6
assert d[MyClass1(n=1, s='a')] == 1
assert d[MyClass1(n=2, s='a')] == 2
assert d[MyClass1(n=2, s='b')] == 3
assert d[MyClass2(n=1, my_class_1=MyClass1(n=1, s='a'))] == 4
assert d[MyClass2(n=2, my_class_1=MyClass1(n=1, s='a'))] == 5
assert d[MyClass2(n=2, my_class_1=MyClass1(n=2, s='a'))] == 6
# Due to `frozen=True`
o = MyClass1(n=1, s='a')
try:
o.n = 2
except FrozenInstanceError as e:
pass
else:
raise 'error'
正如我们在这个例子中看到的,哈希值是基于对象的内容计算的,而不仅仅是基于实例的地址。这就是为什么会有这样的事情:
d = {}
d[MyClass1(n=1, s='a')] = 1
assert d[MyClass1(n=1, s='a')] == 1
即使第二个MyClass1(n=1, s='a')是一个与第一个完全不同的实例,具有不同的地址,也可以工作。
frozen=True是强制的,否则类是不可哈希的,否则用户可能会在容器用作键后修改对象而无意中使容器不一致。更多文档:https://docs.python.org/3/library/dataclasses.html
在Python 3.10.7, Ubuntu 22.10上测试。