Python "триплет" словарь?

Если у нас есть (a1, b1) и (a2, b2) легко использовать словарь для хранения соответствий:

dict[a1] = b1
dict[a2] = b2

И мы можем получить (a1, b1) и (a2, b2) обратно без проблем.

Но если у нас есть (a1, b1, c1) и (a2, b2, c2), возможно ли получить что-то вроде:

dict[a1] = (b1, c1)
dict[b1] = (a1, c1)

Где мы можем использовать a1 или b1 чтобы вернуть триплет (a1, b1, c2)? Имеет ли это смысл? Я не совсем уверен, какой тип данных использовать для этой проблемы. Выше будет работать, но будут дубликаты данных.

По сути, если у меня есть триплет, какой тип данных я могу использовать, чтобы я мог использовать либо первое, либо второе значение, чтобы вернуть триплет?

Ответы

Ответ 1

Решение

Вы можете написать свою собственную структуру данных отображения, которая позволяет add тройки или группы любого размера и восстанавливать группу с помощью __getitem__.

class GroupMap:
    def __init__(self):
        self.data = {}

    def add(self, group):
        for item in group:
            self.data[item] = group

    def __getitem__(self, item):
        return self.data[item]

group = (1, 2, 3)
group_map = GroupMap()

group_map.add(group)

print(group_map[1]) # (1, 2, 3)

Обратите внимание, что эту GroupMap можно использовать для групп любого размера, а не только для троек.

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

теория

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

Предположим, у вас есть граф, содержащий n вершин. Тогда для связности графа необходимо иметь как минимум n - 1 ребер. В приведенной выше структуре данных я использовал n entry в dict, что означает, что решение является почти оптимальным.

Почему бы не использовать n - 1 записей, если это можно сделать? Потому что тогда вам нужно будет пройти весь свой график, чтобы восстановить всю группу. Таким образом, использование еще одного ребра позволяет искать O (1), что является компромиссом, который вы, вероятно, захотите принять.

Ответ 2

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

class TupleDict(dict):

    def __setitem__(self, key, value):
        assert isinstance(key, tuple)
        for i, e in enumerate(key):
            dict.__setitem__(self, e, key[:i] + key[i+1:] + (value,))
        dict.__setitem__(self, value, key)

а затем назначить любой ключ, который является кортежем, одному значению (не уверен, что мне нравится этот синтаксис, но мы можем сделать его другим или использовать автономный метод)

d = TriDict()
d[(1,2)] = 4

и вы получите результат __getitem__ вернет остальную часть кортежа, которого нет.

>>> print(d[1])
(2, 4)
>>> print(d[2])
(1, 4)
print(d[4])
>>> (1, 2)

Ответ 3

Опираясь на ответ Оливье Мелансонса, я придумал это - на случай, если значение значения в кортеже имеет значение:

class GroupMap:
    def __init__(self, data=None):
        self.data = {}
        if data:
            self.add(data)

    def add(self, data):
        for idx, key in enumerate(data):
            self.data.setdefault(idx, {})[key] = data

    def __getitem__(self, key):
        # lookup in first index
        return self.getby(0, key)

    def getby(self, idx, key):
        return self.data[idx].get(key)


data = ('a', 'b', 'c')
g = GroupMap(data)
more_data = ('b', 'a', 'z')
g.add(more_data)

assert g['a'] == data

assert g.getby(0, 'a') == data
assert g.getby(0, 'b') == more_data
assert g.getby(0, 'c') is None

assert g.getby(1, 'a') == more_data
assert g.getby(1, 'b') == data

assert g.getby(2, 'c') == data
assert g.getby(2, 'z') == more_data

assert id(data) == id(g['a']) == id(g.getby(1, 'b'))

Ответ 4

Словари могут хранить только пары ключ-значение.

Однако вы можете создать свой собственный словарь триплетов, используя перегрузку операторов, чтобы при индексировании с любым членом триплетов вы возвращали два других, возможно, что-то вроде этого:

class trictionary:
    def __init__(self):
        self.data = []

    def add(self, group):
        self.data.append(group)

    def __getitem__(self, key):
        for group in data: #Find the set the key belongs to.
            if key in group:
                return tuple(group)

Это позволяет избежать репликации данных и обладает необходимыми функциями за счет производительности. Там может быть лучший способ сделать то же самое.

Ответ 5

Новый тип данных не нужен, давайте просто использовать словарь кортежей:

>>> things['a1'] = ('b1', 'c1')
>>> things['a1']
('b1', 'c1')
>>> ('a1',) + things['a1']
('a1', 'b1', 'c1')

Позвольте поставить функцию вокруг последней части:

>>> def restore_triplet(key,hash):
...     return (key,) + hash[key]
...
>>> restore_triplet('a1',things)
('a1', 'b1', 'c1')

У меня было другое решение только со словарями, которое вы можете найти в истории редактирования, но, вероятно, это самое простое решение, которое можно найти.

Ответ 6

В вашем вопросе есть примеры, которые отличаются от основного вопроса:

По сути, если у меня есть триплет, какой тип данных я могу использовать, чтобы я мог использовать либо первое, либо второе значение, чтобы вернуть триплет?

Дикт. Назначьте пары ключ-значение для element triplet (см. Ответ @Olivier Melançon):

Код

d = {}
for x in triplet:
   d[x] = triplet

демонстрация

d["a"]
# ('a', 'b', 'c')

d["b"]
# ('a', 'b', 'c')

d["c"]
# ('a', 'b', 'c')

ОП требует ясности в отношении предпочтительного поведения в:

  • добавление элементов, например, d[a1] = (b1, c1) против f((a1, b1, c1))
  • упорядочение элементов, например (a1, b1, c1) и (b1, a1, c1)
  • повторяющиеся данные, например трижды удерживайте (a1, b1, c1) или комбинации из двухнаборов подмножеств

С учетом этих пунктов возможны более сложные решения.