2013-02-13 6 views
2

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

similarities = { 
p1: {p2: v12, p3:v13, p4:v14}, 
p2: {p1: v21, p3:v23, p4:v24}, 
p3: {p1: v31, p2:v32, p4:v34}, 
p4: {p1: v41, p2:v42, p4:v43} 
} 

Заметим, что измерение подобия является симметричным. Следовательно, similarities[p1][p2] является таким же, как similarities[p2][p1], то есть v12 == v21.

Иногда мне необходимо устранить p2 от similarities[p1]; и при этом мне нужно будет удалить p1 и p2 со всех внутренних словарей в similarities.
Это утомительно и неэффективно.

Таким образом, вместо того, чтобы поддерживать симметричный словарь, существует ли способ поддерживать словарь с составным ключом, чтобы я мог искать similarities[p1,p2]?

Я не могу использовать tuple с (p1, p2) != (p2, p1) и не знаю априори, как заказать кортеж.

frozenset единственный другой контейнер, который я могу думать, но это не будет сокращать его, так как там может быть и другие ключи в similarities, которые содержат либо p1 или p2 в качестве компонента. Итак, какой контейнер я мог использовать для решения этой проблемы?

Техническая информация:

  • питона 2,7
  • всегда будет ровно два элемента в этой "составной ключ"

Спасибо

+2

'frozenset' кажется, что это позволит решить проблему для меня - Любая причина думать, что там может быть что-то лучше ? – mgilson

+0

Можете ли вы использовать синтаксис, подобный «сходствам [p1, p2]»? – Blender

+0

@Blender: нет, это незаконный синтаксис и в лучшем случае может быть переведен в кортеж – inspectorG4dget

ответ

1

Я думаю, что использование frozenset является единственным логическим решением. Вы можете найти ключи, которые соответствуют только один из значений с помощью понимания с помощью теста множество пересечений:

def remove_ab(ab, similarities): 
    return {k:v for k, v in similarities.items() if not ab & k} 

similarities = {frozenset({1, 2}): "v12", 
       frozenset({1, 3}): "v13", 
       frozenset({2, 3}): "v23", 
       frozenset({3, 4}): "v34"} 

similarities = remove_ab(frozenset({1, 2}), similarities 
print(similarities) # output is {frozenset({3, 4}): 'v34'} 
2

Я бы, наверное, просто использовать frozenset, считая объекты хешируемыми.

В качестве альтернативы, если у них есть четко определенный и последовательный порядок на них, вы можете сохранить их в кортеже, отсортированном по указанному порядку. Вы можете написать небольшой подкласс dict, чтобы сделать это прозрачно, если хотите.

Или вы могли бы сделать что-то вроде этого:

class SymmetricDict(dict): 
    def __getitem__(self, key): 
     if key in self: 
      return dict.__getitem__(self, key) 
     a, b = key 
     return dict.__getitem__(self, (b, a)) 

и аналогично для __setitem__.

+0

Я думаю, вам, вероятно, понадобится 'dict .__ getitem __ (self, key)' вместо 'self [key]', чтобы избежать бесконечного цикла – mgilson

+0

@mgilson Конечно, я хотел написать это, а затем мои пальцы только набрали неправильную вещь. :) Исправлена. – Dougal

+0

To 'super' или не' super' ... Это вопрос ... ;-). (Лично я не использую 'super') – mgilson

0

Если объекты p_ имеют тип, который поддерживает сортировку, можете ли вы использовать кортеж с двумя элементами всегда в lo -> hi order?

+0

Но это не решает проблему удаления всех ключей, которые имеют этот' p_object' как часть составного – inspectorG4dget

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