2010-08-04 1 views
17

Если у меня есть два одинаковых набора, то есть a == b дает мне True, будет ли у них одинаковый порядок итераций? Я попробовал, и он работает:Итерационный порядок множеств в Python

>>> foo = set("abc") 
>>> bar = set("abc") 
>>> zip(foo, bar) 
[('a', 'a'), ('c', 'c'), ('b', 'b')] 

Мой вопрос: было ли мне повезло или это поведение гарантировано?

+0

Если 'а есть b' я думаю, что они будут иметь тот же порядок итераций. Опять же, это не очень тонкая точка = p – katrielalex

ответ

21

Это не было просто совпадение, что они вышли одинаково: реализация оказывается детерминированной, поэтому создание одного и того же набора дважды приводит к тому же заказу. Но Python этого не гарантирует.

Если вы создаете тот же набор двумя различными способами:

n = set("abc") 
print n 

m = set("kabc") 
m.remove("k") 
print m 

... вы можете получить различный порядок:

set(['a', 'c', 'b']) 
set(['a', 'b', 'c']) 
+0

+1 Простой контрпример. – katrielalex

+0

Еще один очень хороший встречный пример. Благодаря! –

+0

Вы совершенно правы: это было не случайно. Например, кажется, что если вы создаете один и тот же набор напрямую, не снимая ничего, вы всегда получаете одинаковый порядок: например: set («abbacca») дает set ('a', 'c', 'b') также как установлено («bbabbca»). Такое поведение является нестохастическим и связано с реализацией. Было бы интересно посмотреть на исходный код python :) (Но во всех случаях было бы неплохо полагаться на него :)) – ThR37

4

Вам повезло, заказ не гарантируется. Единственное, что гарантировано, это то, что наборы будут иметь одинаковые элементы.

Если вам нужна какая-то предсказуемость, вы можете отсортировать их следующим образом: zip(sorted(foo), sorted(bar)).

0

Я бы сказал, что вам повезло. Хотя, возможно, также, что, поскольку элементы в наборе были одинаковыми, они были сохранены в том же порядке. Это поведение не то, на что вы бы хотели положиться.

4

No .:

>>> class MyStr(str): 
...  def __hash__(self): 
...    return 0 
... 
>>> a = MyStr("a") 
>>> b = MyStr("b") 
>>> c = MyStr("c") 
>>> foo = { a, b, c } 
>>> foo 
{'c', 'b', 'a'} 
>>> bar = { b, a, c } 
>>> foo is bar 
False 
>>> foo == bar 
True 
>>> list(zip(foo, bar)) 
[('c', 'c'), ('b', 'a'), ('a', 'b')] 

P.S. Я не знаю, требуется ли переопределение __hash__. Я просто попробовал что-то, что, как я думал, сломал бы это, и все.

+0

Ну, это все же доказывает. Если есть хеш-коллизии, порядок, вероятно, будет зависеть от чего-то, кроме моего контроля. Благодаря! –

1

Да, вам повезло. Смотрите, например:

import random 
r = [random.randint(1,10000) for i in range(20)] 
foo = set(r) 
r.sort(key=lambda _: random.randint(1,10000)) 
bar = set(r) 
print foo==bar 
print zip(foo, bar) 

Который дал мне результат:

True 
[(3234, 3234), (9393, 9393), (9361, 1097), (1097, 5994), (5994, 2044), (1614, 1614), (6074, 4377), (4377, 9361), (5202, 5202), (2355, 2355), (1012, 1012), (7349, 7349), (6198, 6198), (8489, 8489), (7929, 7929), (6556, 6074), (6971, 6971), (2044, 6556), (7133, 7133), (383, 383)]