Использование set как способ изменить порядок повторяемости - PullRequest
0 голосов
/ 14 февраля 2020

У меня есть два кортежа a и b, а также словарь d

a=('C', 'G')
b=('G', 'C') 
d={('G', 'C'): 'T'}

Я хочу, чтобы d[a] и d[b] вернули 'T'.

Для этого я использовал set(), потому что думал, что set() исправляет порядок элементов итерируемого объекта (и удаляет дубликаты, но меня это не волнует).

Так я и сделал :

tuple(set(a)) in d  # return True
tuple(set(b)) in d  # return True
# I did tuple(set()) because set() alone is not hashable and so cannot be used directly

Это работает, пока не работает. И вот я обнаружил, что иногда

set(('C', 'G'))
{'C', 'G'}

, а иногда это не

set(('C', 'G'))
{'G', 'C'}

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

Итак, мои вопросы:

  • Почему это не так?
  • Как я могу решить мою начальную проблему?

Ответы [ 3 ]

0 голосов
/ 14 февраля 2020

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

a = ('C', 'G')
b = ('G', 'C')
d = {tuple(sorted(a)): 'T'}
for tup in [a, b]:
   print( tup, d[tuple(sorted(tup))] )
0 голосов
/ 15 февраля 2020

Одним из решений является нормализация ключей путем их сортировки. Когда мы смотрим вверх, мы также сортируем ключи перед поиском.

import collections.abc

def _normalize(key):
    """ Normalize a key (tuple) by sorting """
    key = tuple(sorted(key))
    return key

class TupleKeyDict(collections.abc.MutableMapping):
    def __init__(self, *args, **kwargs):
        temp_dict = dict(*args, **kwargs)
        self.dict = {
            _normalize(key): value
            for key, value in temp_dict.items()
        }

    def __getitem__(self, key):
        value = self.dict[_normalize(key)]
        return value

    def __setitem__(self, key, value):
        self.dict[_normalize(key)] = value

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

    def __iter__(self):
        return iter(self.dict)

    def __len__(self):
        return len(self.dict)

    def __repr__(self):
        return repr(self.dict)
>>> tkd = TupleKeyDict(d)
>>> tkd
{('C', 'G'): 'T'}

>>> tkd['C', 'G']
'T'

>>> tkd['G', 'C']
'T'

>>> tkd.get(('X', 'Y')) is None
True

>>> tkd['X', 'C'] = 'cx'
>>> tkd['C', 'X']
'cx'

Обсуждение

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

Метод __init__ принимает все, что вы можете передать в dict(). Метод __repr__ помогает при отладке. Все остальные методы соответствуют требованиям collections.abc.MutableMapping.

0 голосов
/ 14 февраля 2020

Набор построен на некоторой структуре данных, которая обеспечивает эффективные проверки членства. Следствием этого является то, что Python теряет исходный порядок, указанный в коде при создании этой структуры.

Если у вас действительно есть только две метки, я бы, вероятно, просто явно указал оба значения.

a=('C', 'G')
b=('G', 'C') 
d={a: 'T', b: 'T'}

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

lbls = ['a', 'b', 'c']
{(f, s):'T' for f in lbls for s in lbls if f != s}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...