如何与字典中的值交换键?


97

我收到一个字典作为输入,并想返回一个字典,其键将是输入的值,而键的值将是对应的输入键。价值观是独一无二的。

例如,说我的输入是:

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

我希望我的输出是:

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

为了澄清,我希望我的结果等于以下内容:

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

有什么精巧的Pythonic方式可以做到这一点?


1
如果您使用的是Python 3,请参见stackoverflow.com/questions/1087694/…,以获得相同的问题,这是一个很好的答案
Stephen Edmonds

@Stephen:请查看投票数第二高的答案,该答案与您链接到的问题中被接受的答案相同。人群更喜欢其他答案……
Roee Adler

4
Python不是perl,python不是ruby。可读性很重要。稀疏胜于密集。鉴于此,这些答案的所有方法都不好。问题中的一个是最好的方法。
o0'。

Answers:


148

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
尽管这似乎是正确的,但添加一个解释其工作原理而不只是代码的方法确实很棒。
2015年

4
如果值不是唯一的怎么办?那么键应该是一个列表...例如:d = {'a':3,'b':2,'c':2} {v:k表示d.iteritems()中的k,v} { 2:'b',3:'a'}应为{2:['b','c'],3:'a'}
Hanan Shteingart

4
@HananShteingart:OP的问题陈述值是唯一的。请为您的案例创建一个单独的问题帖子(最好在此处将其链接给其他人)。
liori

python2代码可以正常工作...但是列表解析缺少[]。列表解析不需要[]吗?
Trevor Boyd Smith,

@TrevorBoydSmith:这不是列表理解,这是一个生成器表达式
liori

55
new_dict = dict(zip(my_dict.values(), my_dict.keys()))

4
是否真的保证values()和keys()具有相同的顺序?
Lennart Regebro

1
是的,来自python.org/dev/peps/pep-3106该规范隐含了.keys()、. values()和.items()返回项目的顺序是相同的(就像在Python中一样) 2.x),因为顺序全部来自dict迭代器(假定是任意的,但只要不修改dict即可保持稳定)。但是此答案需要两次调用my_dict(一个用于值,一个用于键)。也许这不是理想的。
sunqiang

4
是的,这个答案遍历了字典两次。对于大型词典,sunqiang的答案更可取,因为它只需要进行一次迭代。
卡尔·梅耶

@Carl Meyer:也同意,他使用的itertools对于大型数据集要好得多。尽管我不知道最终的dict()调用是否也在流式传输,或者它是否首先汇编了整个对列表
Javier

@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()以任意顺序返回项目,因此您获得了重复值的任意键。
蚂蚁阿斯玛09年

我认为它将采用找到的最后一个键,值对。就像a ['x'] =3。然后您设置a ['x'] =
4。– riza


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}。新字典只能有一个带有key的项目1
  2. 一个或多个值是不可散列的。例如{'one':[1]}[1]是有效值,但不是有效键。

有关此主题的讨论,请参见python邮件列表上的该线程


另外,关于确保原始字典中的值唯一的注释也要+1;否则,您将在“反向”字典中被覆盖...而这(我刚刚为此付出了代价)可能会导致代码中出现棘手的错误!
monojohnny 2011年

15

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


4
dict不保证其values()和keys()将以相同顺序返回元素。同样,keys(),values()和zip()返回一个列表,其中一个迭代器就足够了。
liori

19
@liori:你错了。如果您不修改对values()和keys()的调用之间的dict,则dict保证其values()和keys()的顺序相同。该文档在此处指出:(请阅读“注释”部分:docs.python.org/library/stdtypes.html#dict.items) ),并且itervalues()的调用无需对字典进行任何中间修改,列表将直接对应。”
nosklo

1
好的,那我错了...我还没有检查在线文档。感谢您指出这一点。
liori

您可以使用迭代器itertools.izip而不是zip来使此答案更有效。
阿拉斯戴尔

还有iterkeys和itervalues。但是也可以使用iteritems()
nosklo

15

当前的主要答案假设值是唯一的,但并非总是如此。如果值不是唯一的怎么办?您将失去信息!例如:

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

这应该是正确的答案,因为它涵盖了更一般的情况
Leon Rai

这次真是万分感谢!我失去了其他解决方案的信息。
凸轮

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 Comprehensions(PEP 274)也适用于Python 2.7。
阿森尼

10

扩展Ilya Prokin响应的另一种方法是实际使用该reversed功能。

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

本质上,您的字典通过(使用.items())进行迭代,其中每个项目都是键/值对,并且这些项目与reversed函数交换。当将其传递给dict构造函数时,它将把它们变成您想要的值/键对。


6

改善哈维尔答案的建议:

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

而不是d.keys()您只能编写d,因为如果您使用迭代器浏览字典,它将返回相关字典的键。

例如 对于这种行为:

d = {'a':1,'b':2}
for k in d:
 k
'a'
'b'


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())

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()就足够了。


1

Hanan的答案是正确的,因为它涵盖了更一般的情况(其他答案对于没有意识到重复情况的人来说是一种误导)。对Hanan答案的一种改进是使用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]}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.