按照定义,目前 Vector2d 实例是不可散列的,因此不能放入集合( set )中:
class Vector2d:
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
# 同前章节内容
......
v1 = Vector2d(3, 4)
hash(v1)
# Traceback (most recent call last):
# File "/Users/...", line 97, in <module>
# print(hash(v1))
# TypeError: unhashable type: 'Vector2d'
为了把 Vector2d 实例变成可散列的,必须实现 __hash__ 方法( 还需要 __eq__ 方法,前面已经实现了 )。
目前,我们可以为分量赋新值,如 v1.x = 7,Vector2d 类的代码并不阻止这么做,为此,我们要先把 x 和 y 属性设为只读特性:
class Vector2d:
def __init__(self, x, y):
# 使用两个前导下划线,把方法或属性标记为私有的。
self.__x = float(x)
self.__y = float(y)
# @property 装饰器把读值方法标记为特性,读值方法与公开属性同名,都是 x。
@property
def x(self):
return self.__x
@property
def y(self):
return self.__x
# 省略重复内容
...
然后再实现 __hash__() :
# 在Vector2d类中定义
def __hash__(self):
hash_ = super(Vector2d, self).__hash__()
return hash_
然后,Vector2d 实例就变成可散列的了:
v1 = Vector2d(3, 4)
v2 = Vector2d(5, 6)
print(hash(v1)) # -9223372036572724136
print(hash(v2)) # 282051676
⚠️ 总结:要想创建可散列的类型,不一定要实现特性,也不一定要保护实例属性。只需正确地实现 __hash__ 和 __eq__ 方法即可。但 是,实例的散列值绝不应该变化,因此我们借机提到了只读特性。