2015-10-28 3 views
0

У меня есть два набора с пользовательскими объектами в них. Я беру объекты из одного набора и добавляю их в другой набор с помощью set.update.набор python, кажется, содержит два одинаковых объекта

Впоследствии, кажется, что один набор содержит два одинаковых объекта: их хэш идентичен, они == друг к другу, а не! = Друг к другу. Если я передам набор в список и вернусь к набору, у меня будет только один объект в новом наборе. У меня нет других потоков или процессов, которые могут каким-то образом изменить состояние какого-либо объекта.

Я мог опубликовать мой хэш и эк, но они называют множественным других суб-объекты хэш и эк и было бы много кода, чтобы включить.

Вместо здесь код отладки Я бегу и его выход:

print('old hash', map(hash, node.incoming_edges)) 
print('new hash', map(hash, new_node.incoming_edges)) 
if len(new_node.incoming_edges) > 1: 
    node1, node2 = list(new_node.incoming_edges) 
    print('eq', node1 == node2) 
    print('ne', node1 != node2) 
print('type', type(node.incoming_edges)) 
print('type', type(new_node.incoming_edges)) 

new_node.incoming_edges.update(node.incoming_edges) 

print('combined') 
if len(new_node.incoming_edges) > 1: 
    node1, node2 = list(new_node.incoming_edges) 
    print('eq', node1 == node2) 
    print('ne', node1 != node2) 

print('combined hash', map(hash, new_node.incoming_edges)) 
print('len', len(new_node.incoming_edges)) 

new_node.incoming_edges = set(list(new_node.incoming_edges)) 

print('len', len(new_node.incoming_edges)) 

и соответствующий выход:

old hash [5805087492197093178] 
new hash [5805087492197093178] 
type <type 'set'> 
type <type 'set'> 
combined 
eq True 
ne False 
combined hash [5805087492197093178, 5805087492197093178] 
len 2 
len 1 

Я думал, что, так как мои объекты являются рекурсивными графики, хэш может измените их, добавив их в набор, однако я печатаю хэш до и после операции, чтобы подтвердить, что хеш не изменяется.

Как это может произойти? Я был бы рад представить больше отладочной продукции, я могу легко воспроизвести.

P.S. Вот некоторая информация из PDB в то время как я пытался понять, что происходит:

57      print('type', type(new_node.incoming_edges)) 
58  
59      import pdb; pdb.set_trace() 
60  
61      new_node.incoming_edges.update(node.incoming_edges) 
62 ->     new_node.outgoing_edges.update(node.outgoing_edges) 
63      # new_node.incoming_edges = set(list(new_node.incoming_edges)) 
64  
65      print('combined') 
66      if len(new_node.incoming_edges) > 1: 
67       node1, node2 = list(new_node.incoming_edges) 
(Pdb) !len(new_node.incoming_edges) 
2 
(Pdb) !x, y = new_node.incoming_edges 
(Pdb) x 
<Edge user.id getters={<SQLQuery tables:users; selects:users.last_name; where:{} input_mapping:{'id': 'users.id'}, <SQLQuery tables:users; selects:users.first_name; where:{} input_mapping:{'id': 'users.id'}} setter=None out=False> 
(Pdb) y 
<Edge user.id getters={<SQLQuery tables:users; selects:users.last_name; where:{} input_mapping:{'id': 'users.id'}, <SQLQuery tables:users; selects:users.first_name; where:{} input_mapping:{'id': 'users.id'}} setter=None out=False> 
(Pdb) hash(x) 
-8545778292158950550 
(Pdb) hash(y) 
-8545778292158950550 
(Pdb) x == y 
True 
(Pdb) x != y 
False 
(Pdb) len(set(list(new_node.incoming_edges))) 
1 
(Pdb) len(new_node.incoming_edges) 
2 
+0

Вы говорите, что размещение ваших '__hash__' и' __eq__' будет очень много, так как они заканчивают проверку множества подобъектов. Какие существуют субобъектные проверки? В частности, выполняете ли вы какие-либо сравнения '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' – user2357112

+2

Что-то здесь с вашим примером. Этот питон 3? Не может быть, ваши 'print' будут печатать объекты 'map', а не списки. Этот питон 2? Не может быть, ваши 'print' будут печатать кортежи. – roippi

ответ

2

Psychic отладка: У вас есть set членов в node, которые были добавлены до начала этого кода, а затем мутировали таким образом, что приводит к изменению их хэши. set кэширует хэш-значение каждого объекта при вставке и не перефразирует при нормальных условиях; на самом деле, копирование или обновление с set до set также может избежать повторного перехвата, так как он может скопировать кеш-значение хэша напрямую, а не переигрывать.

Вы «исправили» проблему при преобразовании в list (который эффективно разделяет кеш-хэш-значения), а затем обратно на set (который теперь должен перефразировать все элементы, что заставляет его исправлять дублирование).

Конечно, это не настоящая проблема. Реальное исправление: NEVER сделать изменяемые элементы хешируемые; следовать руководству Python и либо использовать только неизменяемые типы, либо разрешить преобразование из изменчивых в неизменные варианты, где только неизменные варианты определяют __hash__. Если вы определяете __hash__, вы подписываетесь на правила для типов хеширования, которые включают в себя логическую неизменность (и неизменность для всех атрибутов, которые также участвуют в вычислении хэша).

+0

Удивительный, спасибо. Это звучит правильно.Я знал, что наборы всегда должны включать только неизменяемые объекты, но по какой-то причине не соединяли точки, что это было бы причиной этой ошибки. Я пройду и поставлю '__hash__' на неизменяемые объекты. благодаря –

Смежные вопросы