Как мне обмениваться ключами со значениями в словаре?

97

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

Например, скажем, я ввел:

a = dict()
a['one']=1
a['two']=2

Я бы хотел, чтобы мой результат был:

{1: 'one', 2: 'two'}

Чтобы уточнить, я хотел бы, чтобы мой результат был эквивалентом следующего:

res = dict()
res[1] = 'one'
res[2] = 'two'

Любой аккуратный питонический способ добиться этого?

Рои Адлер
источник
1
См. Stackoverflow.com/questions/1087694/… для получения идентичного вопроса, на который есть хороший ответ, если вы используете Python 3
Стивен Эдмондс
@Stephen: см. Второй по количеству голосов ответ, он такой же, как принятый в вопросе, на который вы ссылались. Хотя толпа предпочла другой ответ ...
Рои Адлер
4
Python - это не Perl, python - это не рубин. Читаемость имеет значение. Лучше разреженное, чем плотное. Учитывая это, все методы этих ответов просто плохи ™; тот, который в вопросе, - лучший способ пойти.
о0 '.
3
возможный дубликат обратного / обратного отображения Python
Кори

Ответы:

149

Python 2:

res = dict((v,k) for k,v in a.iteritems())

Python 3 (спасибо @erik):

res = dict((v,k) for k,v in a.items())
Лиори
источник
14
Хотя это кажется правильным, действительно полезно добавить объяснение того, как это работает, а не просто код.
Уилл
4
Что делать, если значения не уникальны? Тогда ключи должны быть списком ... например: d = {'a': 3, 'b': 2, 'c': 2} {v: k для k, v в d.iteritems ()} { 2: 'b', 3: 'a'} должно быть {2: ['b', 'c'], 3: 'a'}
Ханан Штейнгарт
4
@HananShteingart: заявленные значения OP уникальны. Пожалуйста, создайте отдельный вопросный пост для вашего случая (и желательно дайте ссылку на него другим людям).
liori
код python2 работает ... но в понимании списка отсутствуют [и ]. Разве для понимания списка не требуется [и ]?
Trevor Boyd Smith
@TrevorBoydSmith: это не понимание списка, это выражение генератора .
liori
55
new_dict = dict(zip(my_dict.values(), my_dict.keys()))
Хавьер
источник
4
Действительно ли values ​​() и keys () гарантированно имеют одинаковый порядок?
Леннарт Регебро, 06
1
да, из python.org/dev/peps/pep-3106 Спецификация подразумевает, что порядок, в котором элементы возвращаются .keys (), .values ​​() и .items (), такой же (как и в Python 2.x), потому что порядок является производным от итератора dict (который предположительно является произвольным, но стабильным до тех пор, пока dict не изменяется). но для этого ответа нужно дважды вызвать my_dict (один для значений, один для ключей). может это не идеально.
sunqiang 06
4
Да, этот ответ повторяется дважды. Ответ sunqiang предпочтительнее для большого словаря, поскольку он требует только одной итерации.
Карл Мейер,
@Carl Meyer: согласен, он также использует itertools, которые намного лучше подходят для больших наборов данных. хотя мне интересно, является ли последний вызов dict () также потоковым, или он сначала собирает весь список пар
Хавьер
@CarlMeyer дополнительно для n> 1e6 (или 1e9) использование памяти также будет очень большим ... а также замедлит это кучу.
Trevor Boyd Smith
47

Начиная с Python 2.7, включая 3.0+, существует, возможно, более короткая и более читаемая версия:

>>> my_dict = {'x':1, 'y':2, 'z':3}
>>> {v: k for k, v in my_dict.items()}
{1: 'x', 2: 'y', 3: 'z'}
Тихий призрак
источник
30
In [1]: my_dict = {'x':1, 'y':2, 'z':3}

In [2]: dict((value, key) for key, value in my_dict.iteritems())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}
Суньцян
источник
3
Не в исходном вопросе, мне просто любопытно, что произойдет, если у вас будут повторяющиеся значения в исходном словаре, а затем поменять местами ключи / значения с помощью этого метода?
Андре Миллер
2
@Andre Miller: Требуется последнее вхождение конкретного ключа: dict (((1,3), (1,2))) == {1: 2}
balpha
2
дубликаты будут перезаписаны последним обнаруженным дубликатом.
Christopher
2
@Andre Miller: И поскольку d.items () возвращает элементы в произвольном порядке, вы получаете произвольный ключ для повторяющихся значений.
Ants Aasma 06
Я думаю, что потребуется последняя найденная пара ключ, значение. Это похоже на ['x'] = 3. Затем вы устанавливаете ['x'] = 4.
riza
28

Вы можете использовать dict-понимание :

res = {v: k for k, v in a.iteritems()}

Отредактировано: для Python 3 используйте a.items()вместо a.iteritems(). Обсуждения различий между ними можно найти в iteritems в Python на SO.

Акавалл
источник
18

Вы можете попробовать:

d={'one':1,'two':2}
d2=dict((value,key) for key,value in d.iteritems())
d2
  {'two': 2, 'one': 1}

Помните, что вы не можете "перевернуть" словарь, если

  1. Одно и то же значение имеет несколько ключей. Например {'one':1,'two':1}. В новом словаре может быть только один элемент с ключом 1.
  2. Одно или несколько значений не поддаются хэшированию. Например {'one':[1]}. [1]- допустимое значение, но не действительный ключ.

См. Эту ветку в списке рассылки python для обсуждения этой темы.

Alasdair
источник
Также +1 к примечанию об уникальности значений в исходном слове; в противном случае вы получите перезаписи в «перевернутом» слове ... и это (я только что обнаружил, что это по моей цене) может вызвать хитрые ошибки в вашем коде!
monojohnny 06
15

res = dict(zip(a.values(), a.keys()))

pkit
источник
4
dict не гарантирует, что его values ​​() и keys () вернут элементы в том же порядке. Кроме того, keys (), values ​​() и zip () возвращают список, в котором достаточно итератора.
liori
19
@liori: Вы ошибаетесь. dict гарантирует, что его values ​​() и keys () БУДУТ в одном порядке, если, конечно, вы не измените dict между вызовами values ​​() и keys (). В документации указано, что здесь: (прочтите часть «Примечание»: docs.python.org/library/stdtypes.html#dict.items ) «Если items (), keys (), values ​​(), iteritems (), iterkeys ( ), и itervalues ​​() вызываются без каких-либо изменений в словаре, списки будут напрямую соответствовать. "
nosklo
1
Хорошо, тогда я ошибаюсь ... Я не проверял онлайн-документы. Спасибо, что указали на это.
liori
Вы можете использовать итератор itertools.izip вместо zip, чтобы сделать этот ответ более эффективным.
Alasdair
И iterkeys, и itervalues. Но с таким же успехом можно использовать iteritems ()
nosklo
14
new_dict = dict( (my_dict[k], k) for k in my_dict)

или даже лучше, но работает только в Python 3:

new_dict = { my_dict[k]: k for k in my_dict}
бальфа
источник
2
На самом деле Dict Computing ( PEP 274 ) также работает с Python 2.7.
Арсений
14

Текущий ведущий ответ предполагает, что значения уникальны, что не всегда так. Что делать, если значения не уникальны? Вы потеряете информацию! Например:

d = {'a':3, 'b': 2, 'c': 2} 
{v:k for k,v in d.iteritems()} 

возвращается {2: 'b', 3: 'a'}.

Информация о 'c'была полностью проигнорирована. В идеале должно было быть что-то вроде {2: ['b','c'], 3: ['a']}. Это то, что делает нижняя реализация.

Python 2.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.iteritems():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv

Python 3.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.items():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv
Ханан Штейнгарт
источник
это должен быть правильный ответ, поскольку он охватывает более общий случай
Леон Рай
Спасибо тебе за это! Я терял информацию с другими решениями.
10

Еще один способ расширить ответ Ильи Прокина - это фактически использовать reversedфункцию.

dict(map(reversed, my_dict.items()))

По сути, ваш словарь повторяется (с использованием .items()), где каждый элемент является парой ключ / значение, и эти элементы меняются местами с reversedфункцией. Когда это передается dictконструктору, он превращает их в пары значение / ключ, которые вам нужны.

Солнечный Патель
источник
6

Предложение по улучшению ответа Хавьера:

dict(zip(d.values(),d))

Вместо того, d.keys()чтобы написать простоd , потому что если вы пройдете по словарю с итератором, он вернет ключи соответствующего словаря.

Ex. для этого поведения:

d = {'a':1,'b':2}
for k in d:
 k
'a'
'b'
тень2097
источник
3

Это легко сделать с пониманием словаря:

{d[i]:i for i in d}
user10084443
источник
2
dict(map(lambda x: x[::-1], YourDict.items()))

.items()возвращает список кортежей (key, value). map()проходит по элементам списка и применяется lambda x:[::-1]к каждому его элементу (кортежу), чтобы отменить его, так что каждый кортеж становится (value, key)в новом списке, выплевывающемся из карты. Наконец, dict()делает диктант из нового списка.

Илья Прокин
источник
.items () возвращает список кортежей (ключ, значение). map () проходит по элементам списка и применяется lambda x:[::-1]к каждому его элементу (кортежу), чтобы отменить его, так что каждый кортеж становится (значением, ключом) в новом списке, выплевывающемся из карты. Наконец, dict () делает диктант из нового списка.
Илья Прокин
1

Использование цикла : -

newdict = {} #Will contain reversed key:value pairs.

for key, value in zip(my_dict.keys(), my_dict.values()):
    # Operations on key/value can also be performed.
    newdict[value] = key
Девеш Сайни
источник
1

Если вы используете Python3, он немного отличается:

res = dict((v,k) for k,v in a.items())
Gravity Grave
источник
1

Добавление решения на месте:

>>> d = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
>>> for k in list(d.keys()):
...     d[d.pop(k)] = k
... 
>>> d
{'two': 2, 'one': 1, 'four': 4, 'three': 3}

В Python3 очень важно использовать, list(d.keys())потому что dict.keysвозвращает представление ключей. Если вы используете Python2, этого d.keys()достаточно.

Timgeb
источник
1

Ответ Ханана правильный, поскольку он охватывает более общий случай (другие ответы вводят в заблуждение тех, кто не подозревает о повторяющейся ситуации). Улучшение ответа Ханана - использование setdefault:

mydict = {1:a, 2:a, 3:b}   
result = {}
for i in mydict:  
   result.setdefault(mydict[i],[]).append(i)
print(result)
>>> result = {a:[1,2], b:[3]}
пегах
источник