简体   繁体   English

python 可变和可散列类型

[英]python mutable and hashable types

Can anyone please explain how is it possible to get an object which is both hashable and mutable?谁能解释一下如何获得既可散列又可变的 object 吗?

I have seen: Hashable, immutable it does not answer my question我见过: Hashable, immutable它不能回答我的问题

I heard it is possible in python.我听说在 python 中是可能的。

Here is some code that shows you the effects of making an object both hashable and mutable.下面是一些代码,向您展示了使 object 既可散列又可变的效果。 Note that the link you provided does actually answer your question in Andrew Jaffe's answer and the comments under it;请注意, 您提供的链接确实在 Andrew Jaffe 的答案及其下方的评论中回答了您的问题; I've added some code from this question about hashing in order to help explain.为了帮助解释,我从这个问题中添加了一些关于散列的代码。

The default for a python object's hash value is the object ID, which will not change during it's lifetime. python 对象的 hash 值的默认值为 object ID,在其生命周期内不会更改。 A custom value can be provided by __hash__ ; __hash__可以提供自定义值; however, in order to be useful, this must be translated into something that can be hashable, such as an integer or a string.然而,为了有用,它必须被翻译成可以散列的东西,例如 integer 或字符串。

class test():
    use_int = 0
    use_name = ""
    use_list = []

    def __init__(self, use_int:int, use_name:str, use_list:list)->None:
        self.use_int = use_int
        self.use_name = use_name
        self.use_list = use_list
    
    # Compact the attributes into a tuple of int and strings
    # Without changing the list into a string, the hash will fail
    def __key(self):
        return (str(self.use_int), self.use_name,",".join(self.use_list))
    
    # The above step could be done here with a small object like this    
    def __hash__(self):
        return hash(self.__key())
    
    # For fun: try changing this to "__repr__"
    def __str__(self):
        return ",".join(self.__key())

Let's run this through and see what the outcomes are:让我们运行一下,看看结果是什么:

      
if __name__ == "__main__":
    # Initialise our object
    test_obj = test(0,"John",["test","more test",])

Anytime we want to look at the hash values, we can use print(test_obj.__hash__()) .每当我们想查看 hash 值时,我们都可以使用print(test_obj.__hash__()) Try changing the int and seeing if the hash changes.尝试更改int并查看 hash 是否更改。 Also, since Python uses a random salt with str hashes to prevent collisions, note also that hashing this way will supply different hash values in different processes.此外,由于 Python 使用带有 str 散列的随机盐来防止冲突,还要注意这种散列方式将在不同的进程中提供不同的 hash 值。

We can demonstrate that the object is usable as a hashable object by testing if a dictionary will accept the object as a key.我们可以通过测试字典是否接受 object 作为键来证明 object 可用作可散列的 object。 Dictionary keys cannot be lists, for example.例如,字典键不能是列表。

    test_dict = dict()
    test_dict[test_obj] = "first object used as key"
    print(test_dict)

Try changing the list within the object, and seeing if it is still acceptable:尝试更改 object 中的列表,看看它是否仍然可以接受:

    test_obj.use_list.append("yet more tests")
    test_dict[test_obj] = "second object used as key"
    print(test_dict)

What if we need to go back?如果我们需要 go 回来怎么办?

    del test_obj.use_list[-1]
    test_dict[test_obj] = "third object used as key"
    print(test_dict)

Note how "first object" has been changed to "third object".请注意“第一个对象”如何更改为“第三个对象”。

Putting all of this code together:将所有这些代码放在一起:

class test():
    
    use_int = 0
    use_name = ""
    use_list = []
    
    def __init__(self, use_int:int, use_name:str, use_list:list)->None:
        self.use_int = use_int
        self.use_name = use_name
        self.use_list = use_list
    
    def __key(self):
        return (str(self.use_int), self.use_name,",".join(self.use_list))
        
    def __hash__(self):
        return hash(self.__key())
    
    def __str__(self):
        return ",".join(self.__key())
        
if __name__ == "__main__":
    test_obj = test(0,"John",["test","more test",])
    print(test_obj.__hash__())
    test_obj.use_int = 1
    print(test_obj.__hash__())
    test_obj.use_int = 2
    print(test_obj.__hash__())
    test_dict = dict()
    test_dict[test_obj] = "object used as key"
    print(test_dict)
    test_obj.use_list.append("yet more tests")
    test_dict[test_obj] = "second object"
    print(test_dict)
    del test_obj.use_list[-1]
    test_dict[test_obj] = "third object"
    print(test_dict)
    print(test_obj)
    test_obj.use_int = 1
    print(test_obj.__hash__())

But what if we need to have a consistent, predictable hash value?但是,如果我们需要一个一致的、可预测的 hash 值怎么办? __hash() doesn't have to use hash() . __hash()不必使用hash() It can return other values.它可以返回其他值。 This would mean making the process compatible though - otherwise you'll get TypeError: __hash__ method should return an integer .不过,这意味着使流程兼容-否则您将得到TypeError: __hash__ method should return an integer

Try converting the name into an integer:尝试将名称转换为 integer:

    def __key(self):
        name_number = 0
        for c in self.use_name:
            name_number += ord(c)
        return self.use_int + name_number

    def __hash__(self):
        return self.__key()
    
    def __str__(self):
        return str(self.__key())

What happens if you run the dictionary tests in this scenario?如果在这种情况下运行字典测试会发生什么?

You'll notice that instead of having two entries to the dictionary, you only get one - this is because changing the list does not change the hash value produced by the object.您会注意到字典中没有两个条目,而是只有一个 - 这是因为更改列表不会更改 object 生成的 hash 值。

Outcome of the original random hash dict tests:原始随机 hash dict 测试的结果:

{< main .test object at 0x7f05bc1f1fd0>: 'first object'} {< main .test object at 0x7f05bc1f1fd0>: '第一个对象'}
{< main .test object at 0x7f05bc1f1fd0>: 'object used as key', < main .test object at 0x7f05bc1f1fd0>: 'second object'} {< main .test object at 0x7f05bc1f1fd0>: 'object used as key', < main .test object at 0x7f05bc1f1fd0>: 'second object'}
{< main .test object at 0x7f05bc1f1fd0>: 'third object', < main .test object at 0x7f05bc1f1fd0>: 'second object'} {< main .test object at 0x7f05bc1f1fd0>: '第三个对象', < main .test object at 0x7f05bc1f1fd0>: '第二个对象'}

Outcome of the second fixed hash dict tests:第二个固定 hash dict 测试的结果:

{< main .test object at 0x7fc7b5510fd0>: 'first object'} {< main .test object at 0x7fc7b5510fd0>: '第一个对象'}
{< main .test object at 0x7fc7b5510fd0>: 'second object'} {< main .test object at 0x7fc7b5510fd0>: '第二个对象'}
{< main .test object at 0x7fc7b5510fd0>: 'third object'} {< main .test object at 0x7fc7b5510fd0>: '第三个对象'}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM