Невозможно узнать, как это проще в ваших ситуациях, поскольку кода вообще нет, но лучше вариантов, вероятно, будет
При необходимости сделайте их статическими
Вы можете создать свой собственный тип словаря путем подкласса dict
и добавления требуемой логики. Вот основной пример:
class TwoWayDict(dict):
def __setitem__(self, key, value):
# Remove any previous connections with these values
if key in self:
del self[key]
if value in self:
del self[value]
dict.__setitem__(self, key, value)
dict.__setitem__(self, value, key)
def __delitem__(self, key):
dict.__delitem__(self, self[key])
dict.__delitem__(self, key)
def __len__(self):
"""Returns the number of connections"""
return dict.__len__(self) // 2
И он работает так:
>>> d = TwoWayDict()
>>> d['foo'] = 'bar'
>>> d['foo']
'bar'
>>> d['bar']
'foo'
>>> len(d)
1
>>> del d['foo']
>>> d['bar']
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
KeyError: 'bar'
Я уверен, что я не рассматривал все случаи, но это должно заставить вас начать .
Другим возможным решением является реализация подкласса dict
, который содержит оригинальный словарь и отслеживает его обратную версию. Сохранение двух отдельных dicts может быть полезно, если клавиши и значения перекрываются.
class TwoWayDict(dict):
def __init__(self, my_dict):
dict.__init__(self, my_dict)
self.rev_dict = {v : k for k,v in my_dict.iteritems()}
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.rev_dict.__setitem__(value, key)
def pop(self, key):
self.rev_dict.pop(self[key])
dict.pop(self, key)
# The above is just an idea other methods
# should also be overridden.
Пример:
>>> d = {'a' : 1, 'b' : 2} # suppose we need to use d and its reversed version
>>> twd = TwoWayDict(d) # create a two-way dict
>>> twd
{'a': 1, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b'}
>>> twd['a']
1
>>> twd.rev_dict[2]
'b'
>>> twd['c'] = 3 # we add to twd and reversed version also changes
>>> twd
{'a': 1, 'c': 3, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b', 3: 'c'}
>>> twd.pop('a') # we pop elements from twd and reversed version changes
>>> twd
{'c': 3, 'b': 2}
>>> twd.rev_dict
{2: 'b', 3: 'c'}
Нет, нет никакого способа сделать это, не создавая двух словарей. Как можно было бы реализовать это с помощью всего одного словаря, продолжая предлагать сопоставимую производительность?
Вам лучше создать настраиваемый тип, который инкапсулирует два словаря и предоставляет нужные функции.
Вот еще одна двухсторонняя реализация словаря, расширяя класс pythons dict
в случае, если вам не понравилось ни одно из этих других:
class DoubleD(dict):
""" Access and delete dictionary elements by key or value. """
def __getitem__(self, key):
if key not in self:
inv_dict = {v:k for k,v in self.items()}
return inv_dict[key]
return dict.__getitem__(self, key)
def __delitem__(self, key):
if key not in self:
inv_dict = {v:k for k,v in self.items()}
dict.__delitem__(self, inv_dict[key])
else:
dict.__delitem__(self, key)
Используйте его как обычный словарь на языке python, за исключением конструкция:
dd = DoubleD()
dd['foo'] = 'bar'
Я бы просто заполнил второй хеш с помощью
reverse_map = dict((reversed(item) for item in forward_map.items()))
reverse_map = dict(reversed(item) for item in forward_map.items())
– drozzy
12 December 2011 в 19:30
my_dict.update(dict(reversed(item) for item in my_dict.items()))
– Gilly
1 June 2017 в 01:50
Модуль расширения kjbuckets C предоставляет структуру данных «graph», которая, как я считаю, дает вам то, что вы хотите.
В вашем специальном случае вы можете хранить оба в одном словаре:
relation = {}
relation['Alice'] = 'Bob'
relation['Bob'] = 'Alice'
Поскольку то, что вы описываете, является симметричным отношением. A -> B => B -> A
Я знаю, что это более старый вопрос, но я хотел упомянуть еще одно отличное решение этой проблемы, а именно пакет python bidict . Очень просто использовать:
from bidict import bidict
map = bidict(Bob = "Alice")
print(map["Bob"])
print(map.inv["Alice"])
Вы можете использовать DoubleDict
, как показано в рецепте 578224 в поваренной книге Python .
У вас есть две отдельные проблемы.
Сделайте что-то вроде этого
from collections import defaultdict
switchboard= defaultdict( list )
x = Conversation( "Alice", "Bob" )
y = Conversation( "Alice", "Charlie" )
for c in ( x, y ):
switchboard[c.p1].append( c )
switchboard[c.p2].append( c )
В pypi есть библиотека расширенных коллекций: https://pypi.python.org/pypi/collections-extended/0.6.0
Использование класса биекций как легко:
RESPONSE_TYPES = bijection({
0x03 : 'module_info',
0x09 : 'network_status_response',
0x10 : 'trust_center_device_update'
})
>>> RESPONSE_TYPES[0x03]
'module_info'
>>> RESPONSE_TYPES.inverse['network_status_response']
0x09
Два хэш-карты на самом деле, вероятно, являются самым быстродействующим решением, предполагающим, что вы можете сэкономить память. Я бы обернул их в один класс - бремя на программиста состоит в том, чтобы убедиться, что два хэш-карты синхронизируются правильно.
mydict[:value]
для получения key
(за счет некоторой производительности)
– Tobias Kienzler
29 May 2013 в 08:18
.add
, чтобы вы могли делать такие вещи, какd.add('Bob', 'Alice')
, вместо использования синтаксиса, который я показал. Я бы также включил некоторые обработки ошибок. Но вы получаете основную идею. :) – Sasha Chedygov 8 November 2012 в 00:46d['foo'] = 'baz'
потребуется дополнительно удалить клавишуbar
). – beardc 29 January 2013 в 21:10dict
создает некоторые вводящие в заблуждение поведение здесь, потому что, если вы создадите объект с каким-то начальным контентом, структура будет нарушена.__init__
необходимо переопределить, чтобы конструкция вродеd = TwoWayDict({'foo' : 'bar'})
работала правильно. – Henry Keiter 4 June 2014 в 18:20pip install bidict
. URL: pypi.python.org/pypi/bidict – user1036719 24 March 2015 в 07:44