为什么即使PYTHONHASHSEED = 0,Python集的顺序也不是确定的?

时间:2018-08-21 12:03:12

标签: python python-3.x hash set

我正在开发一个基于代理的模型,在该模型中,我使用不同类型的代理类,这些类的实例分配给不同类型的对象,例如学校,公司,房屋等。我遇到的问题是我无法确保运行的可重复性在调试时,由于模型的复杂性,使得任务非常艰巨。经过长时间的调查,我意识到问题与集合的顺序有关(当然会应用内置的随机种子和numpy随机种子)。即使当我将PYHTONHASHSEED = 0设置时,我也会注意到每次运行时集合的顺序都是随机的。当代理移动时,这会使我的模型的每次运行都不同。

我当然知道集合并不意味着要有顺序。我想使用它们使从物体上移除代理的模型轻巧快速。我希望它们随机运行,除非需要调试引发异常的特定运行。

我添加以下代码,以便可以验证我的主张。在启动代码之前,我总是通过导出从命令行设置PYTHONHASHSEED。我从代码中打印了PYTHONHASHSEED值,以检查该值确实已更新

import os
import random
import numpy as np

print('PYTHON HASH SEED IS', os.environ['PYTHONHASHSEED'])

random.seed(1)
np.random.seed(2)

class S:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return "".join([type(self).__name__, "_{0.a!r}_",
                        "School", "_{0.b!r}" ]).format(self)

list1 = np.random.randint(1, 100,size=40)
list2 = np.random.randint(1, 10,size=40)
d1 = dict()
s1 = set()
d1['students'] = s1
# assign students to d1
for s_id, sch_id in zip(list1, list2):
    d1['students'].add(S(s_id, sch_id))

print(s1)

奇怪的是,如果我使用整数作为集合成员而不是类实例,则无法检测到随机性。问题是否与set成员是类实例这一事实有关?为什么?

当然,我可以重新建模将代理分配给模型对象的方式,并用列表替换集,但是如果可能的话,我想了解这个问题。我使用的版本是python 3.5.4

1 个答案:

答案 0 :(得分:1)

您要存储的对象(类型为S)来自未提供__eq____hash__覆盖的类,因此they use the default implementation, which is object identity based:< / p>

  

默认情况下,用户定义的类具有__eq__()__hash__()方法;使用它们,所有对象比较不相等(除了它们本身),并且x.__hash__()返回一个适当的值,使得x == y暗示x is yhash(x) == hash(y)

对象标识(作为CPython的实现细节)等效于分配对象的内存地址(原始指针值),并且分配器每次运行都将返回不同的地址,因此每次的顺序将不同时间。 int不存在此问题,因为它们具有基于非身份的相等性和哈希值;它们是根据值而不是标识进行散列的,因此确切的内存地址是无关紧要的。

要获得具有固定种子的自定义类的一致顺序,您需要定义特殊的等式和哈希方法,例如:

def __hash__(self):
    return hash((self.a, self.b))

def __eq__(self, other):
    if not isinstance(other, S):
        return NotImplemented
    return self.a == other.a and self.b == other.b