如何在字典中交换键和值?

151

我收到一个字典作为输入,希望返回一个新的字典,其键将是输入值,值将是相应的输入键。值是唯一的。

例如,假设我的输入是:

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

我希望我的输出结果是:

{1: 'one', 2: 'two'}
为了澄清,我希望我的结果与以下内容等效:
res = dict()
res[1] = 'one'
res[2] = 'two'

有没有简洁的Pythonic方式可以实现这个?


1
如果你正在使用Python 3,可以查看http://stackoverflow.com/questions/1087694/how-to-swap-keys-for-values-in-a-dictionary,那里有一个很好的答案。 - Stephen Edmonds
@Stephen:看看第二个得票最多的答案,它与你链接到的问题中所接受的那个答案是一样的。然而人们更喜欢另一个答案... - Roee Adler
5
Python 不是 Perl,Python 不是 Ruby。可读性很重要。稀疏比密集好。鉴于此,这些答案的所有方法都只是不好的™;问题中所提供的方法是最好的选择。 - o0'.
19个回答

203

Python 2:


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

17
虽然这似乎是正确的,但最好添加一段说明它是如何工作的,而不仅仅是代码。 - Will
6
如果值不唯一,那么键应该是一个列表...例如:d = {'a':3, 'b': 2, 'c': 2} {v:k for k,v in d.iteritems()} {2: 'b', 3: 'a'} 应该变为 {2: ['b','c'], 3: 'a'} - Hanan Shteingart
5
请为您的情况创建一个单独的问题帖子(最好在此处附上链接,以供其他人查看)。@HananShteingart:OP的问题陈述了价值观是独特的。 - liori
Python2的代码可以运行...但列表推导式缺少[]。列表推导式不需要[]吗? - Trevor Boyd Smith
1
两件事情:1)如果您的原始值重复,它将覆盖先前的值,您最终会得到一个较短的字典;2)如果“值”不可哈希化-例如数组-,它将失败。 - AlexD
显示剩余3条评论

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

8
values() 和 keys() 是否保证有相同的顺序? - Lennart Regebro
2
规范意味着 .keys()、.values() 和 .items() 返回项目的顺序相同(就像在 Python 2.x 中一样),因为顺序都来自字典迭代器(假定字典未被修改,则迭代器是任意但稳定的)。但是,这个答案需要两次调用 my_dict(一次获取值,一次获取键)。也许这不是理想的。 - sunqiang
5
是的,这个答案需要两次遍历字典。针对大型字典,sunqiang的答案更好,因为它只需要一次遍历。 - Carl Meyer
@Carl Meyer:同意,另外,他正在使用itertools,这对于大数据集来说要好得多。虽然我想知道最后的dict()调用是否也是流式的,还是它首先组装了整个对列表。 - Javier
@TrevorBoydSmith:在Python 3中,.values().keys()是支持dict的视图,而zip是一个惰性迭代器;除了所需的新dict之外,不会产生额外的副本。如果您仍在使用Python 2,则可以使用.itervalues().iterkeys()(或者完全省略.keys(),因为dict已经是它们的键的可迭代对象),并使用itertools.izip而不是zip来避免临时变量(例如,dict(itertools.izip(my_dict.itervalues(), my_dict))将不涉及任何有意义的临时内存开销)。 - ShadowRanger
显示剩余2条评论

61

从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'}

36

你可以使用字典推导式

Python 3

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

Python 2

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

编辑:对于Python 3,请使用a.items()而不是a.iteritems()。关于它们之间的区别的讨论可以在SO上的iteritems in Python找到。


34
In [1]: my_dict = {'x':1, 'y':2, 'z':3}

Python 3

In [2]: dict((value, key) for key, value in my_dict.items())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}

Python 2

In [2]: dict((value, key) for key, value in my_dict.iteritems())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}

3
如果在原始字典中存在重复的值,并使用这种方法交换键/值,会发生什么?(提示:可能会导致一些键丢失,因为重复的值只能拥有一个键。) - Andre Miller
2
@Andre Miller:它获取特定键的最后一个出现:dict(((1,3),(1,2))) == {1:2} - balpha
2
重复的文本将被覆盖为最后一次遇到的重复内容。 - Christopher
2
@Andre Miller:由于d.items()以任意顺序返回项目,因此对于重复值,您会得到一个任意的键。 - Ants Aasma
我认为它会取到它找到的最后一个键值对。就像 a['x'] = 3。然后你设置 a['x'] = 4。 - riza
显示剩余3条评论

24

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

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

1
这应该是正确的答案,因为它涵盖了更一般的情况。 - Leon Rai
1
谢谢您!其他解决方案让我丢失了信息。 - user7313804

19

你可以尝试:

Python 3

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

Python 2

->

Python 2

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]是有效值,但无法作为键使用。

请查看此讨论线程以了解更多信息。


另外,关于确保原始字典中的值唯一的注释也需要+1;否则,您将在“反转”的字典中得到覆盖...我刚刚发现这会导致代码中棘手的错误! - monojohnny

19

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


4
dict不能保证它的values()和keys()方法返回的元素顺序相同。而且,keys()、values()和zip()方法返回的是一个列表,而一个迭代器就足够了。 - liori
21
@liori: 你错了。dict保证在调用values()和keys()时会按相同的顺序返回,当然前提是在两次调用之间没有修改过字典。文档在这里声明了这一点:(请阅读“注意”部分:http://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

16

扩展 Ilya Prokin 的回答 的另一种方法是实际使用 reversed 函数。

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

本质上,你的字典通过迭代(使用.items())进行操作,其中每个项目都是键/值对,这些项目被reversed函数交换。当传递给dict构造函数时,它将它们转换为值/键对,这就是你想要的。


15
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}

3
实际上,字典推导式(PEP 274)也适用于Python 2.7。 - Arseny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接