如何在更改对象属性后保留用户定义对象集中元素的唯一性
How to retain uniqueness of elements inside Set of user-defined objects after changing attribute of object
如何保留 Set 的唯一性特性,以便在将自定义实例添加到 Set 后修改属性?
就像下面的代码一样:
Person "Jack" 和 "John" 在相等性 "Name" 方面是不同的。所以他们都被添加到集合中
但是如果我将人名“Jack”更改为“John”,那么 2 个实例 jack 和 john 将相等
但是我的 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
您创建了两个不同的对象,如果您打印 set1
,您将得到如下内容:{<__main__.Person object at 0x7f8dfbfc5e10>, <__main__.Person object at 0x7f8dfbfe2a10>}
虽然它们的属性名称不同,但它们仍然是两个不同的对象,保存在不同的内存空间中。这就是为什么当你把它们放在一个集合中时,你会有意想不到的行为,仍然有它们!
当您执行 jack.name = "John"
时,您只是在更改属性 self.name
。
为了得到你想要的结果你必须做:set1 = {jack.name, john.name}
它会return你{'John'}
您应该只使用 set
的不可变对象或引用。见 Python docs:
Having a __hash__()
implies that instances of the class are immutable.
您 set
中的 Person
对象是可变的,但您已经实现了自己的散列函数和相等函数来解决这个问题,正如您所指出的那样,绕过了安全性。
我认为定义自定义散列函数和相等函数很好,但它们应该始终 return 相同,无论您对它们引用的内容做什么:例如,使用 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}
...再一次,它会重新计算集合。
如何保留 Set 的唯一性特性,以便在将自定义实例添加到 Set 后修改属性?
就像下面的代码一样: Person "Jack" 和 "John" 在相等性 "Name" 方面是不同的。所以他们都被添加到集合中 但是如果我将人名“Jack”更改为“John”,那么 2 个实例 jack 和 john 将相等 但是我的 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
您创建了两个不同的对象,如果您打印 set1
,您将得到如下内容:{<__main__.Person object at 0x7f8dfbfc5e10>, <__main__.Person object at 0x7f8dfbfe2a10>}
虽然它们的属性名称不同,但它们仍然是两个不同的对象,保存在不同的内存空间中。这就是为什么当你把它们放在一个集合中时,你会有意想不到的行为,仍然有它们!
当您执行 jack.name = "John"
时,您只是在更改属性 self.name
。
为了得到你想要的结果你必须做:set1 = {jack.name, john.name}
它会return你{'John'}
您应该只使用 set
的不可变对象或引用。见 Python docs:
Having a
__hash__()
implies that instances of the class are immutable.
您 set
中的 Person
对象是可变的,但您已经实现了自己的散列函数和相等函数来解决这个问题,正如您所指出的那样,绕过了安全性。
我认为定义自定义散列函数和相等函数很好,但它们应该始终 return 相同,无论您对它们引用的内容做什么:例如,使用 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}
...再一次,它会重新计算集合。