【问题标题】:Is set.pop() deterministic?set.pop() 是确定性的吗?
【发布时间】:2012-05-13 00:08:40
【问题描述】:

我了解 python 集合的元素没有排序。调用pop方法返回任意元素;我没问题。

我想知道的是,当集合具有相同的历史时,pop 是否总是返回相同的元素。当然,在一个版本的 python 中,我不介意 python 的不同版本/实现是否做自己的事情。特别是,我问的是python 2.7。在这种情况下,这不仅仅是 api 的实现问题。

我在游戏的程序地牢生成器中大量使用集合,我希望结果对于给定的种子具有确定性。

【问题讨论】:

  • 为什么不测试一下/看源码?
  • @delnan " 特别是,我问的是 python 2.7。在这种情况下,这不仅仅是 api 的实现问题。"因此,无需按照您的建议测试多个版本或未来版本。您似乎已经想到了便携性和永恒性的要求。
  • @Marcin 啊,是的,对不起。我错过了。这是一种下意识的反应,许多像这样的 cmets 实际上都有我错误地指责你的缺陷(“在 C89 中是……未定义的行为吗?”——“只要在你的机器上尝试你的编译器版本,使用您碰巧选择的标志,使其工作”)。
  • 如果你有 exact 相同的对象集,并且可以保证使用相同的哈希函数,那么是的,set.pop(),以及list(set())可以是确定性的。

标签: python set


【解决方案1】:

一般的答案是否。@Christophe 和@Marcin (un) 有帮助地指出的python 源代码表明,元素是按照它们在哈希表中出现的顺序弹出的。因此,弹出顺序(可能是迭代顺序)确定性的,但仅适用于固定哈希值。 根据__hash__ 文档中的注意,数字是这种情况,但字符串不是,顺便说一下,这也直接涉及您的问题:

请注意,默认情况下,str、bytes 和 datetime 对象的 hash() 值是用不可预测的随机值“加盐”的。尽管它们在单个 Python 进程中保持不变,但在 Python 的重复调用之间它们是不可预测的。

[...]

更改哈希值会影响 dicts、sets 和其他映射的迭代顺序。 Python 从未对这种顺序做出保证(它通常在 32 位和 64 位版本之间变化)。

编辑:正如@Marcin 指出的那样,我引用的链接不适用于 Python 2。 哈希随机化became the default with Python 3.3. Python 2.7 默认情况下没有有意的非确定性字符串哈希。

通常,对于散列不是其值的可重复函数的任何对象(例如,如果散列基于内存地址),这都是一个问题。但相反,如果您为集合中的对象定义自己的 __hash__ 方法,则可以预期它们将以可重现的顺序返回。 (前提是该系列的历史和平台保持不变)。

【讨论】:

【解决方案2】:

如果您确实针对某个特定版本的 python,那么您可以查看源代码并测试其行为(但测试良好 - 考虑负载因素等)。

如果您想要可移植性,或者您发现 set 没有按要求执行,请使用有序字典(这里有一个:http://code.activestate.com/recipes/576693/;还有很多其他的,所以找一个您喜欢的外观),然后适应它作为一个集合。

更新:这是一个有序集:http://packages.python.org/Brownie/api/datastructures.html#brownie.datastructures.OrderedSet

【讨论】:

【解决方案3】:

如果你想强制确定性,你可以尝试类似

value = min(my_set)
my_set.remove(value)

【讨论】:

  • 请注意,这仅在 min() 明确时才具有确定性。可能有一个具有不同值的奇怪集合,其中有两个或多个都小于所有其他值(并且两者都小于另一个)。在野外并不常见,但可能。
  • 一个更好的例子是无法排序的值(即只能比较是否相等)。 __lt__ 的定义允许同时使用 x < yy < x,虽然是合法的,但坦率地说是错误的。
  • 当集合无法排序时(例如,一组复数),您的解决方案无论如何都会失败并出现 TypeError。但是考虑class epsilon(float): def __lt__(self, other): return True if 0 < other
【解决方案4】:

内部我认为情况类似于dict。顺序由散列算法确定,在某些 情况下会产生相同的结果。但是你不应该依赖它,因为一旦元素数量变大,集合就会遇到冲突(即它的内部散列),最终导致不同的排序。

简而言之:不,set.pop() 不是确定性的。不要假设任何顺序,因为 API 明确指出,

一个集合对象是一个无序集合

【讨论】:

    【解决方案5】:

    The documentation 没有指定它必须是确定性的,因此您应该假设它不是。

    【讨论】:

    • 鉴于问题似乎与特定版本有关,因此无需假设任何内容 - 可以检查源代码并测试行为。
    猜你喜欢
    • 1970-01-01
    • 2011-12-31
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 2021-05-26
    • 1970-01-01
    相关资源
    最近更新 更多