问题描述
如何在将自定义实例添加到Set后,保留Set的Uniqueness特性来修改自定义实例的属性?
就像下面的代码: Person "Jack" 和 "John" 在相等性 "Name" 方面是不同的。所以它们都被添加到集合中 但是如果我将 Person "Jack" 的名字改为 "John,那么 2 个实例 jack 和 john 将相等 但是我的 Set 没有反映这一点。他们仍然认为这两个实例是不同的
注意:当有人在将用户定义的实例添加到集合中后不小心修改了它们时,这会导致潜在的错误
我们是否有办法刷新 Set 或如何避免此问题?
class Person:
def __init__(self,name):
self.name = name
def __eq__(self,other):
return self.name == other.name
def __hash__(self):
return hash(self.name)
jack = Person("Jack")
john = Person("John")
set1 = {jack,john}
jack.name = "John"
print(set1) // return 2 instance instead of 1. This is undesired behavior because Now both jack & john are equal
解决方法
您应该只使用 set
的不可变对象或引用。见Python docs:
拥有 __hash__()
意味着类的实例是不可变的。
您的 Person
中的 set
对象是可变的,但您已经实现了自己的散列和相等函数来解决这个问题,绕过安全性,正如您所指出的那样。
我认为定义自定义散列和相等函数是可以的,但无论您对它们引用的内容做什么,它们都应该始终返回相同的内容:例如,使用 ID 或内存地址进行散列。
我建议两种选择中的一种,强烈倾向于第一种:
选项 A:不可变的 Person
在构造时使 Person
不可变。我最喜欢的方法是使用数据类:
from dataclasses import dataclass
@dataclass(frozen=True)
class Person:
name: str
jack = Person("Jack")
john = Person("John")
# Note you don't need to define your own hash method.
set1 = {jack,john}
# This will fail:
jack.name = "Jaques"
# Consider the need for this. But if you have,say,a lot of different
# fields on the Person and want to just change one or a few,try:
import dataclasses
jaques = dataclasses.replace(jack,{"name": "Jaques"})
# But note this is a different object. The set is still the same as before.
# You need to remove "jack" from the set and add "jaques" to it.
选项 B:重新计算集合
我应该指出,我认为这不是一个好主意,但您可以简单地运行:
set1 = {jack,john}
...再次,它将重新计算集合。
,您创建了两个不同的对象,如果您打印 set1
,您将得到如下内容:{<__main__.Person object at 0x7f8dfbfc5e10>,<__main__.Person object at 0x7f8dfbfe2a10>}
即使它们的属性名称不同,它们仍然是保存在不同内存空间中的两个不同对象。这就是为什么当你将它们放入一个集合时,你仍然拥有它们的意外行为!
当您执行 jack.name = "John"
时,您只会更改属性 self.name
。
为了得到你想要的结果,你必须:set1 = {jack.name,john.name}
它会回报你{'John'}