python: пара значений, которые будут называться взаимно? [Дубликат]

Невозможно узнать, как это проще в ваших ситуациях, поскольку кода вообще нет, но лучше вариантов, вероятно, будет

  1. скрыть их в class как частные члены vars.
  2. выставляют их через геттеры.

При необходимости сделайте их статическими

69
задан Peter O. 7 December 2012 в 09:35
поделиться

12 ответов

Вы можете создать свой собственный тип словаря путем подкласса 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'

Я уверен, что я не рассматривал все случаи, но это должно заставить вас начать .

70
ответ дан Sasha Chedygov 20 August 2018 в 18:27
поделиться
  • 1
    @SudhirJonathan: Вы можете пойти гораздо дальше с этой идеей - например, добавьте метод .add, чтобы вы могли делать такие вещи, как d.add('Bob', 'Alice'), вместо использования синтаксиса, который я показал. Я бы также включил некоторые обработки ошибок. Но вы получаете основную идею. :) – Sasha Chedygov 8 November 2012 в 00:46
  • 2
    Думаю, это подпадает под эти дополнения, но было бы полезно удалить старые пары ключей при установке новых (d['foo'] = 'baz' потребуется дополнительно удалить клавишу bar). – beardc 29 January 2013 в 21:10
  • 3
    Обратите внимание, что это работает только в том случае, если ключи и значения никогда не совпадают – Tobias Kienzler 28 May 2013 в 14:39
  • 4
    Также стоит упомянуть: subclassing dict создает некоторые вводящие в заблуждение поведение здесь, потому что, если вы создадите объект с каким-то начальным контентом, структура будет нарушена. __init__ необходимо переопределить, чтобы конструкция вроде d = TwoWayDict({'foo' : 'bar'}) работала правильно. – Henry Keiter 4 June 2014 в 18:20
  • 5
    Просто хочу упомянуть, что для этого есть библиотека: pip install bidict. URL: pypi.python.org/pypi/bidict – user1036719 24 March 2015 в 07:44

Другим возможным решением является реализация подкласса 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'}
1
ответ дан Akavall 20 August 2018 в 18:27
поделиться

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

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

4
ответ дан Andrew Hare 20 August 2018 в 18:27
поделиться

Вот еще одна двухсторонняя реализация словаря, расширяя класс 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'
0
ответ дан browlm13 20 August 2018 в 18:27
поделиться

Я бы просто заполнил второй хеш с помощью

reverse_map = dict((reversed(item) for item in forward_map.items()))
19
ответ дан Ian Clelland 20 August 2018 в 18:27
поделиться
  • 1
    Там есть дополнительные скобки: reverse_map = dict(reversed(item) for item in forward_map.items()) – drozzy 12 December 2011 в 19:30
  • 2
    Хороший простой способ, если вы больше не будете обновлять dict. Я использовал my_dict.update(dict(reversed(item) for item in my_dict.items())) – Gilly 1 June 2017 в 01:50

Модуль расширения kjbuckets C предоставляет структуру данных «graph», которая, как я считаю, дает вам то, что вы хотите.

0
ответ дан Kragen Javier Sitaker 20 August 2018 в 18:27
поделиться
  • 1
    Извините, я не упоминал об этом, но его на движке приложения ... так что нет расширений C. – Sudhir Jonathan 22 September 2009 в 06:42

В вашем специальном случае вы можете хранить оба в одном словаре:

relation = {}
relation['Alice'] = 'Bob'
relation['Bob'] = 'Alice'

Поскольку то, что вы описываете, является симметричным отношением. A -> B => B -> A

35
ответ дан Nadia Alramli 20 August 2018 в 18:27
поделиться
  • 1
    Хм ... да, мне нравится этот лучший. Старался избегать делать две записи, но пока это лучшая идея. – Sudhir Jonathan 21 September 2009 в 21:32
  • 2
    Все еще думайте, что должна быть возможна двухсторонняя карта: - / – Sudhir Jonathan 21 September 2009 в 21:37
  • 3
    Если он должен быть эффективным, то под обложками вам нужно, чтобы оба индекса были проиндексированы в какой-либо структуре данных индекса - будь то хеш, отсортированный список, двоичное дерево, trie, массив суффикса, полный sistrings, или что-то еще более экзотический. Простым способом сделать это в Python является использование хэша. – Kragen Javier Sitaker 22 September 2009 в 20:32
  • 4
    @SudhirJonathan Если вы предпочитаете по-настоящему двухстороннюю карту, посмотрите на bidict , как указано, например. в этот вопрос - обратите внимание на проблемы производительности, обсуждаемые Aya в комментариях к моему вопросу об ошибке . – Tobias Kienzler 29 May 2013 в 08:16

Я знаю, что это более старый вопрос, но я хотел упомянуть еще одно отличное решение этой проблемы, а именно пакет python bidict . Очень просто использовать:

from bidict import bidict
map = bidict(Bob = "Alice")
print(map["Bob"])
print(map.inv["Alice"])
5
ответ дан Nearoo 20 August 2018 в 18:27
поделиться

Вы можете использовать DoubleDict, как показано в рецепте 578224 в поваренной книге Python .

1
ответ дан Noctis Skytower 20 August 2018 в 18:27
поделиться

У вас есть две отдельные проблемы.

  1. У вас есть объект «Разговор». Он относится к двум Лицам. Поскольку у человека может быть несколько разговоров, у вас есть отношение «многие ко многим».
  2. У вас есть карта от лица к списку разговоров. Конверсия будет иметь пару лиц.

Сделайте что-то вроде этого

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 )
4
ответ дан S.Lott 20 August 2018 в 18:27
поделиться

В 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
0
ответ дан Schwolop 20 August 2018 в 18:27
поделиться

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

6
ответ дан Triptych 20 August 2018 в 18:27
поделиться
  • 1
    +1, вот что bidict в принципе, плюс сахар для доступа к обратному отображению с помощью mydict[:value] для получения key (за счет некоторой производительности) – Tobias Kienzler 29 May 2013 в 08:18
Другие вопросы по тегам:

Похожие вопросы: