【问题标题】:python mutable and hashable typespython可变和可散列类型
【发布时间】:2021-04-25 05:11:34
【问题描述】:

谁能解释一下如何获得一个既可哈希又可变的对象?

我见过: Hashable, immutable它没有回答我的问题

听说在 python 中可以。

【问题讨论】:

  • 您提供的链接实际上确实回答了您在 Andrew Jaffe 的答案及其下的 cmets 中的问题。您必须定义如何使用 __hash()__ 对对象进行散列。您选择要包含的属性以及如何组合它们 - 这就是 Python 默认使用对象 ID(不会更改)的原因,因为它无法猜测每个可能类的预期哈希值。

标签: python python-3.x mutable hashable


【解决方案1】:

这里有一些代码向您展示了将对象设置为可散列和可变的效果。请注意,the link you provided 实际上确实在 Andrew Jaffe 的答案及其下的 cmets 中回答了您的问题;我添加了来自this question about hashing 的一些代码以帮助解释。

python 对象的哈希值的默认值是对象 ID,在其生命周期内不会更改。 __hash__可以提供自定义值;但是,为了有用,必须将其转换为可以散列的东西,例如整数或字符串。

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())

让我们来看看结果如何:

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

任何时候我们想查看哈希值,我们都可以使用print(test_obj.__hash__())。尝试更改int 并查看哈希是否更改。另外,由于 Python 使用带有 str 散列的随机盐来防止冲突,还要注意这种散列方式会在不同的进程中提供不同的散列值。

我们可以通过测试字典是否接受该对象作为键来证明该对象可用作可散列对象。例如,字典键不能是列表。

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

尝试更改对象内的列表,看看是否仍然可以接受:

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

如果我们需要返回怎么办?

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

注意“第一个对象”如何更改为“第三个对象”。

将所有这些代码放在一起:

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__())

但是如果我们需要一个一致的、可预测的哈希值呢? __hash() 不必使用hash()!它可以返回其他值。这将意味着使流程兼容 - 否则您将获得TypeError: __hash__ method should return an 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())

如果您在这种情况下运行字典测试会发生什么?

您会注意到字典中没有两个条目,而是只有一个 - 这是因为更改 列表 不会更改对象生成的哈希值。

原始随机哈希字典测试的结果:

{ma​​in.test object at 0x7f05bc1f1fd0>: 'first object'}
{ma​​in.test object at 0x7f05bc1f1fd0>: 'object used as key', ma​​in.test object at 0x7f05bc1f1fd0>: 'second object'}
{ma​​in.test object at 0x7f05bc1f1fd0>: '第三个对象', ma​​in.test object at 0x7f05bc1f1fd0>: 'second object'}

第二次固定哈希字典测试的结果:

{ma​​in.test object at 0x7fc7b5510fd0>: 'first object'}
{ma​​in.test object at 0x7fc7b5510fd0>: 'second object'}
{ma​​in.test object at 0x7fc7b5510fd0>: 'third object'}

【讨论】:

    猜你喜欢
    • 2021-09-30
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-26
    • 2013-10-22
    • 2013-01-23
    • 2018-03-13
    相关资源
    最近更新 更多