是python';s";“设置”;马厩?

是python';s";“设置”;马厩?,python,set,Python,Set,当回答另一个SO问题()时,问题出现了 当我在一个python集上迭代几次(在调用之间不改变它)时,我能假定它总是以相同的顺序返回元素吗?如果没有,改变顺序的理由是什么?它是确定性的,还是随机的?还是定义了实现 当我重复调用同一个python程序时(不是随机的,不是依赖输入的),我会得到相同的集合顺序吗 根本的问题是,python集合迭代顺序是否仅取决于用于实现集合的算法,还是取决于执行上下文?集合的定义是无序的、唯一的元素()。您应该只关心接口,而不关心实现。如果您想要一个有序的枚举,您可能应

当回答另一个SO问题()时,问题出现了

当我在一个python集上迭代几次(在调用之间不改变它)时,我能假定它总是以相同的顺序返回元素吗?如果没有,改变顺序的理由是什么?它是确定性的,还是随机的?还是定义了实现

当我重复调用同一个python程序时(不是随机的,不是依赖输入的),我会得到相同的集合顺序吗


根本的问题是,python集合迭代顺序是否仅取决于用于实现集合的算法,还是取决于执行上下文?

集合的定义是无序的、唯一的元素()。您应该只关心接口,而不关心实现。如果您想要一个有序的枚举,您可能应该将其放入一个列表中并对其进行排序


Python有许多不同的实现。不要依赖于未记录的行为,因为您的代码可能会在不同的Python实现中中断

关于集合的稳定性没有正式的保证。但是,在CPython实现中,只要集合没有改变,项目将以相同的顺序生成。集合被实现为开放寻址哈希表(带有一个主探测),因此插入或删除项目可以完全改变顺序(特别是当触发调整大小时,它会重新组织项目在内存中的布局)。您也可以有两个相同的集合,但它们以不同的顺序生成项目,例如:

>>> s1 = {-1, -2}
>>> s2 = {-2, -1}
>>> s1 == s2
True
>>> list(s1), list(s2)
([-1, -2], [-2, -1])

除非您非常确定您拥有相同的集合,并且在两次迭代之间没有任何内容涉及到它,否则最好不要依赖它保持不变。例如,对中间调用的函数进行看似无关的更改可能会产生很难发现的bug。

这肯定是实现定义的。政府只说

集合是无序集合,不记录元素位置或插入顺序


为什么不使用创建您自己的OrderedSet类呢?

正如所指出的,这是一个严格的实现细节

但是,只要不改变调用之间的结构,就没有理由让只读操作(=迭代)随着时间而改变:任何正常的实现都不会这样做。即使是可用于实现集合(例如跳过列表)的随机(=非确定性)数据结构,在未发生更改时也不会更改读取顺序

因此,理性地说,你可以放心地依靠这种行为

(我知道某些GCs可能会在后台线程中重新排序内存,但即使是这种重新排序,在数据结构级别上也不会明显,除非出现错误。)

当我调用相同的python时 重复编程(非随机、非随机) 输入相关),我会得到同样的结果吗 订购电视机

经过快速的实验,我现在可以回答这部分问题了。使用以下代码:

class Foo(object) :
  def __init__(self,val) :
    self.val = val
  def __repr__(self) :
    return str(self.val)

x = set()
for y in range(500) :
  x.add(Foo(y))
print list(x)[-10:]
我可以触发我在另一个问题中所问的行为。如果我重复运行此操作,则输出会发生变化,但不是每次运行都会发生变化。它似乎是“弱随机”的,因为它变化缓慢。这当然取决于实现,所以我应该说我正在雪豹上运行macports Python2.6。虽然程序将在长时间内输出相同的答案,但做一些影响系统熵池的事情(写入磁盘通常是有效的)有时会将其踢到不同的输出中


类Foo只是一个简单的int包装器,因为实验表明,这不会发生在int集上。我认为这个问题是由于缺少对象的
\uuuuu eq\uuuu
\uuuu hash\uuuuu
成员造成的,尽管我非常想知道潜在的解释/避免方法。同样有用的方法是重现/重复“坏”运行。有人知道它使用什么种子,或者我如何设置种子吗?

A
set
frozenset
本质上是一个无序的集合。在内部,集合基于a,键的顺序取决于插入顺序和算法。在CPython(又称标准Python)中,小于机器字大小(32位或64位)的整数散列到自身,但文本字符串、
字节
字符串和
日期时间
对象散列到随机变化的整数;您可以通过设置
pythonhasheed
环境变量来控制这一点

\uuuu散列\uuuuu
文档:

注意

默认情况下,
str
字节
日期时间
对象被不可预测的随机值“腌制”。虽然他们 在单个Python进程中保持不变,它们不是 可在重复调用Python之间预测

这旨在提供针对拒绝服务的保护 由精心选择的利用最坏情况的输入引起 dict插入的性能,O(n^2)复杂度。看见 详情请参阅

更改哈希值会影响dict、set和 其他映射。Python从未对这种排序做出过保证 (它通常在32位和64位构建之间变化)

另见蟒蛇种子

散列其他类的对象的结果取决于类的方法的细节

所有这一切的结果是,您可以有两个包含相同字符串的集合,但当您将它们转换为列表时,它们可以进行不相等的比较。或者他们可能不会下面的一些代码演示了这一点。在某些运行中,它将只是循环,不打印任何内容,但在其他运行中,它将快速找到使用与原始文件不同顺序的集合

from random import seed, shuffle

seed(42)

data = list('abcdefgh')
a = frozenset(data)
la = list(a)
print(''.join(la), a)

while True:
    shuffle(data)
    lb = list(frozenset(data))
    if lb != la:
        print(''.join(data), ''.join(lb))
        break    
典型输出

dachbgef frozenset({'d', 'a', 'c', 'h', 'b', 'g', 'e', 'f'})
deghcfab dahcbgef

答案是否定的。

Python集操作不稳定。

我做了一个简单的实验来证明这一点

Th
import random
random.seed(1)

x=[]
class aaa(object):
    def __init__(self,a,b):
        self.a=a
        self.b=b

for i in range(5):
    x.append(aaa(random.choice('asf'),random.randint(1,4000)))

for j in x:
    print(j.a,j.b)

print('====')
for j in set(x):
    print(j.a,j.b)
a 2332
a 1045
a 2030
s 1935
f 1555
====
a 2030
a 2332
f 1555
a 1045
s 1935

Process finished with exit code 0
a 2332
a 1045
a 2030
s 1935
f 1555
====
s 1935
a 2332
a 1045
f 1555
a 2030

Process finished with exit code 0