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)
или комбинации из двухнаборов подмножеств
С учетом этих пунктов возможны более сложные решения.