Обратимый словарь для Python

Я хотел бы сохранить некоторые данные в Python в форме, аналогичной словарю: {1:'a', 2:'b'}. Каждое значение будет уникальным не только среди других значений, но и среди ключей.

Есть ли простая структура данных, которую я могу использовать для получения соответствующего объекта, независимо от того, использую ли я «ключ» или «значение»? Например:

>>> a = {1:'a', 2:'b'}
>>> a[1]
'a'
>>> a['b']
2
>>> a[3]
KeyError

«Ключи» - это стандартные целые числа Python, а значения - короткие (‹256 char) строки.

Мое текущее решение создает перевернутый словарь и выполняет его поиск, если я не могу найти результат в исходном словаре:

pointsreversed = dict((v, k) for k, v in points.iteritems())
def lookup(key):
    return points.get(key) or pointsreversed.key()

Это занимает вдвое больше места, что не очень хорошо (мои словари могут быть до нескольких сотен мегабайт) и в среднем на 50% медленнее.

РЕДАКТИРОВАТЬ: как упоминалось в нескольких ответах, два dicts не удваивают использование памяти, поскольку это только словарь, а не элементы внутри, то есть дублирование.

Есть ли решение, которое улучшит это?


person Alex J    schedule 30.06.2009    source источник
comment
В вашем примере вы действительно имеете в виду, что [1] возвращает «1»? Похоже, вы хотите, чтобы он вернул "а".   -  person    schedule 30.06.2009
comment
(0) pointsreversed.key () ??? - скопируйте / вставьте фактический рабочий код (1) Среднее количество поисков должно быть N * (2-p), где p = вероятность (найдено в 1-м слове); На 50% медленнее подразумевается, что p мало или вы ввели накладные расходы (2). Ваши строки не будут дублироваться, если вы не сделаете что-то экстраординарное, поэтому использование памяти не удвоится. (3) Как так получилось, что вы не знаете, есть ли у вас объект int или str?   -  person John Machin    schedule 30.06.2009


Ответы (7)


Похожие сообщения:

обратное отображение Python

Отображения Python 1: 1

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

person Community    schedule 30.06.2009
comment
Да, если все ключи и значения уникальны, вы / можете / использовать один словарь. Не думал об этом. +1 - person Rick Copeland; 30.06.2009
comment
Он мог, в зависимости от того, чем еще он хотел заниматься ... например, single_dict.items () и друзья могут вызвать проблемы и / или чрезмерное использование isinstance () - person John Machin; 30.06.2009

Если ваши ключи и значения не пересекаются, один из очевидных подходов - просто сохранить их в одном dict. то есть:

class BidirectionalDict(dict):
    def __setitem__(self, key, val):
        dict.__setitem__(self, key, val)
        dict.__setitem__(self, val, key)

    def __delitem__(self, key):
        dict.__delitem__(self, self[key])
        dict.__delitem__(self, key)

d = BidirectionalDict()
d['foo'] = 4
print d[4]   # Prints 'foo'

(Вы также, вероятно, захотите реализовать такие вещи, как методы __init__, update и iter*, чтобы они действовали как настоящий dict, в зависимости от того, сколько функций вам нужно).

Это должно включать только один поиск, хотя может не сэкономить много памяти (в конце концов, у вас все равно вдвое больше записей dict). Однако обратите внимание, что ни этот, ни ваш оригинал не будут занимать вдвое больше места: dict занимает место только для ссылок (фактически указателей), плюс накладные расходы на превышение доступности. Пространство, занимаемое вашими данными, не будет повторяться дважды, поскольку указаны одни и те же объекты.

person Brian    schedule 30.06.2009

В «Искусстве компьютерного программирования» в Vokume 3 Knuth есть раздел, посвященный поиску вторичных ключей. Для целей вашего вопроса значение можно рассматривать как вторичный ключ.

Первое предложение - сделать то, что вы сделали: составить эффективный индекс ключей по значению.

Второе предложение - настроить большое b-дерево, которое является составным индексом кластеризованных данных, где узлы ветвления содержат значения, а листья содержат ключевые данные и указатели на более крупную запись (если она есть).

Если данные геометрические (как у вас), есть вещи, называемые почтовыми деревьями. Он может ответить на такие вопросы, как ближайший объект к точке x. Вот несколько примеров: http://simsearch.yury.name/russir/01nncourse-hand.pdf Другой простой вариант для такого рода запросов - дерево квадрантов и дерево kd. http://en.wikipedia.org/wiki/Quadtree

Другой последний вариант - комбинаторное хеширование, при котором вы объединяете ключ и значение в специальный вид хеша, который позволяет вам выполнять эффективный поиск по хешу, даже если у вас нет обоих значений. Я не смог найти в Интернете хорошее объяснение комбинаторного хеша, но оно есть в TAoCP, Volume 3 Second Edition на странице 573.

Конечно, для некоторых из них вам, возможно, придется написать свой собственный код. Но если память или производительность действительно важны, вы можете не торопиться.

person Christopher    schedule 30.06.2009

Он не должен использовать «вдвое больше места». Словари просто хранят ссылки на данные, а не сами данные. Итак, если у вас есть миллион строк, занимающих миллиард байтов, то каждый словарь может занять дополнительные 10-20 миллионов байтов - крошечную долю от общего хранилища. Использование двух словарей - это правильно.

person user185345    schedule 07.10.2009

Вставьте перевернутую пару (ключ, значение) в один и тот же dict:

a = {1:'a', 2:'b'}
a.update(dict((v, k) for k, v in a.iteritems()))

Тогда вы сможете делать и то, и другое, как вам нужно:

print a[1]
print a['a']
person mtasic85    schedule 30.06.2009

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

А код ...

# search a dictionary for key or value
# using named functions or a class
# tested with Python25 by Ene Uran 01/19/2008

def find_key(dic, val):
    """return the key of dictionary dic given the value"""
    return [k for k, v in symbol_dic.iteritems() if v == val][0]

def find_value(dic, key):
    """return the value of dictionary dic given the key"""
    return dic[key]

class Lookup(dict):
    """
    a dictionary which can lookup value by key, or keys by value
    """
    def __init__(self, items=[]):
        """items can be a list of pair_lists or a dictionary"""
        dict.__init__(self, items)

    def get_key(self, value):
        """find the key(s) as a list given a value"""
        return [item[0] for item in self.items() if item[1] == value]

    def get_value(self, key):
        """find the value given a key"""
        return self[key]
person tgray    schedule 30.06.2009
comment
Но в этом случае у вас нет прямого доступа к значению, так как вам нужно его искать .. Это снижает интерес словаря - person ThibThib; 30.06.2009

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

d = {1: 'a', 2: 'b'}
dict(zip(d.values(), d.keys()))
person ART GALLERY    schedule 03.04.2019