Set of mutable objects gets befuddled

爷,独闯天下 提交于 2021-01-05 06:46:56

问题


I have a class that is compared only based on the content of its attributes, so that two objects with the same values are equivalent.

class a(object):
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return 'a(value={})'.format(self.value)
    def __hash__(self):
        return hash(self.__repr__())
    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.value == other.value
    def __ne__(self, other):
        return not self.__eq__(other)

When I create an object of the class and add it to a set it is added correctly. Then I create a second object with a different value and modify the first one to match the second object. The set now shows the modified object and the comparison between a1 and a2 correctly says true.

If I now try to search in the set for a1 or a2, the set does not manage to find them, even if a1 was explicitly added and a2 compares true.

# create the object and a set with it
a1 = a(1)
a_set = set([a1])
print('Elements in set are: {}'.format(','.join([element.__repr__() for element in a_set])))

# build a new object and modify the original object to be identical to the second
a2 = a(2)
a1.value = 2
print('Elements in set are: {}'.format(','.join([element.__repr__() for element in a_set])))

# object compare fine
print('{} == {} is {}'.format(a1, a2, a1 == a2))

# none of the object are in the set (even if a1 was added to the set)
print('{} in set is {}'.format(a1, a1 in a_set))
print('{} in set is {}'.format(a2, a2 in a_set))
print('Elements in set are: {}'.format(','.join([element.__repr__() for element in a_set])))

I understand that this could be due to a1 being stored in the set under the hash corresponding to having the original value, not the modified value, but the problem in any case is that adding a2 to the set would give me a second element which is equivalent to the first. Is there a way out of this?


回答1:


The hash value of the object is stored the first time the object is stored in the set's hash table. It does not get updated even when the object is mutated.

The initial hash value hash("a(value=1)") is what is in use not hash("a(value=2)").

This is one of the reasons why mutable items are never taken by sets in the first place; except you used a backdoor.

Modifying a1.value = 1 restores your object as the same match as what the set contains.




回答2:


The default python set type does not support mutable objects. You cgould design an interface that kept a set of weak references to containers containing its instances. The instances could then signal the containers when their hash changes. You'd need:

  • A method on instances that containers call when they add the instance

  • a method on containers that instances call when their hash changes

  • Detection of hash changes, possibly overriding setattr on the class.



来源:https://stackoverflow.com/questions/44001750/set-of-mutable-objects-gets-befuddled

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!