反转字典映射

1030

假设有如下字典:

my_map = {'a': 1, 'b': 2}

如何反转该地图以获得以下结果:
inv_map = {1: 'a', 2: 'b'}
33个回答

1453

Python 3+:

inv_map = {v: k for k, v in my_map.items()}

Python 2:

inv_map = {v: k for k, v in my_map.iteritems()}

7
在最近的Python 2.7.x版本中,my_map.items()也可以使用。 - valentin
77
如果值不唯一,那么这个方法就行不通了,这种情况下会丢失一些条目。 - gabuzo
26
有趣的事实:从Python 3.7开始,字典(按插入时间)将被排序。 - byxor
4
是的,作为一项实现细节。这个新实现的保序方面被视为一项实现细节,不应依赖于它。不能编写依赖于Dict具有与OrderedDict相同行为的代码,因为没有保证它会保持这种方式。 - Mattias
19
@Mattias,这适用于Python 3.6。对于版本3.7,保持顺序是官方支持的:https://mail.python.org/pipermail/python-dev/2017-December/151283.html。BDFL说了这样的话。 - interDist
显示剩余3条评论

225
假设字典中的值是唯一的:
Python 3:
dict((v, k) for k, v in my_map.items())

Python 2:

dict((v, k) for k, v in my_map.iteritems())

27
值也必须是可哈希的。 - John La Rooy
35
如果值不唯一,那么无论如何也没有唯一的字典反转,换句话说,进行反转是没有意义的。 - Wrzlprmft
2
@Buttons840 只有最后一个键将出现为该值。iteritems() 输出的顺序可能没有保证,因此可以假定对于非唯一值将分配任意键,以一种在某些条件下明显可重现但通常不是这样的方式。 - Evgeni Sergeev
2
请注意,在Python 3中不再有iteritems()方法,因此这种方法将无法工作;而是使用items()方法,如接受的答案所示。此外,字典推导会使代码比调用dict更简洁。 - Mark Amery
7
对于非唯一值,反函数有一个自然的定义。每个值都被映射到导致它的键集合。 - Leo

196
如果 my_map 中的值不唯一: Python 3:
inv_map = {}
for k, v in my_map.items():
    inv_map[v] = inv_map.get(v, []) + [k]

Python 2:

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, []) + [k]

69
...或者只是inv_map.setdefault(v,[]).append(k)。 我曾经是defaultdict的铁杆粉丝,但后来被多次坑了,得出结论实际上显式比隐式更好。 - alsuren
2
@YaroslavBulatov 不,这里展示的代码没有问题 - inv_map.get(v, []) 如果已经有列表,则返回已添加的列表,因此赋值不会重置为空列表。但是,使用setdefault会更好看一些。 - Mark Amery
15
这里使用一个集合会更合适。键(可能)是可哈希的,且没有顺序。inv_map.setdefault(v, set()).add(k) - Artyer
5
在Python3中,使用my_map.items()代替my_map.iteritems() - apitsch
1
@JuanC.Roldán,这是事实,但这并不是什么大不了的事情。 - juanpa.arrivillaga
显示剩余4条评论

54

如果您希望保留映射类型(假设它是dictdict子类),可以执行以下操作:

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

5
聪明虽然它可能是,但当原始字典中有多个键具有相同的值时,它就无法起作用。 - Rafael_Espericueta
10
对于这个问题的任何可能答案都是正确的,因为具有重复值的地图是不可逆的。 - Mark Amery
3
@Mark_Amery 从更一般的意义上说,它可以是可逆的。例如:D = {1: [1, 2], 2:[2, 3], 3: [1]}, Dinv = {1: [1, 3], 2: [1, 2], 3: [2]}。D是一个字典,例如{parent: children},而Dinv是字典{child: parents}。 - Rafael_Espericueta
3
我认为不需要做f.__class__,因为你已经假设它是一个字典了。我会这样做:dict(map(reversed, f.items()))。该代码将反转字典中的键值对,并返回一个新的字典。 - bkbilly
@bkbilly,我们并没有做出任何假设,只是有一个 items 方法。 - Mr_and_Mrs_D

48

试试这个:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(请注意,Python字典视图的文档明确保证.keys().values()按相同顺序排列其元素,这使得上述方法可行。)
或者:
inv_map = dict((my_map[k], k) for k in my_map)

或者使用Python 3.0的字典推导式

inv_map = {my_map[k] : k for k in my_map}

2
请注意,这仅在键是唯一的情况下才有效(如果要反转它们,这几乎永远不是情况)。 - gented
根据 https://www.python.org/dev/peps/pep-0274/,字典推导在2.7+版本中也是可用的。 - Kawu

42

另外一种更加实用的方法:

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

4
感谢您的发帖。我不确定这是否是首选方案 - 引用Guido Van Rossum在PEP 279中的话:“ filtermap应该消失并被纳入列表推导式中,而不是增加更多变体”。 - Brian M. Hunt
2
是的,布莱恩,你说得很有道理。我只是把它作为谈话的一个点添加进来的。我想大多数人都会觉得字典推导式更易读。(而且我猜也更快) - Brendan Maguire
3
еҸҜиғҪдёҚеҰӮе…¶д»–ж–№ејҸжҳ“иҜ»пјҢдҪҶиҝҷз§Қж–№жі•зҡ„еҘҪеӨ„еңЁдәҺеҸҜд»Ҙе°ҶdictиҪ»жқҫжӣҝжҚўдёәе…¶д»–жҳ е°„зұ»еһӢпјҢдҫӢеҰӮcollections.OrderedDictжҲ–collections.defaultdictгҖӮ - Will S

19

我们也可以使用 defaultdict 来反转拥有重复键的字典:

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

请看这里

使用defaultdict比使用dict.setdefault()实现相同功能更简单且更快。


返回 dict(d_inv) 可以更好,因为字典比不太标准的 defaultdict 有更广泛的支持。例如,一些序列化程序(如 yaml.safe_dump)无法序列化默认字典,而它们可以序列化字典。 - Konstantin

17

这是对Robert的回答进行扩展,适用于字典中的值不唯一的情况。

class ReversibleDict(dict):
    # Ref: https://dev59.com/iHRB5IYBdhLWcg3w3K8J#13057382/
    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.

        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """
        
        revdict = {}
        for k, v in self.items():
            revdict.setdefault(v, []).append(k)
        return revdict

实现有限制,即您不能使用reversed两次并得到原始结果。它不是对称的。它经过Python 2.6测试。这里是我用来打印结果字典的用例。

如果您更喜欢使用set而不是list,并且可能存在无序应用程序,因此这是有意义的,请改用setdefault(v,set()).add(k)而不是setdefault(v,[]) .append(k)


这也是使用集合而不是列表的好地方,即revdict.setdefault(v, set()).add(k) - mueslo
当然,但这正是使用 set 的好理由。它是适用于此处的内置类型。如果我想查找所有值不为 12 的键怎么办?那么我只需执行 d.keys() - inv_d[1] - inv_d[2](在 Python 3 中)。 - mueslo

11

有很多答案,但如果我们谈论的是一个具有非唯一值的词典,那么并没有找到什么干净的东西。

一个解决方案可能是:

from collections import defaultdict

inv_map = defaultdict(list) 
for k, v in my_map.items(): 
    inv_map[v].append(k)

例子:

如果初始字典为 my_map = {'c':1, 'd':5, 'a':5, 'b':10}

运行上述代码将会给出如下结果:

{5: ['a', 'd'], 1: ['c'], 10: ['b']}

11

比如,你有以下字典:

my_dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

你想以倒置的形式得到它:

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

第一种解决方案。要将字典中的键-值对反转,请使用for循环方法:

# Use this code to invert dictionaries that have non-unique values

inverted_dict = dict()
for key, value in my_dict.items():
    inverted_dict.setdefault(value, list()).append(key)

第二种解决方案。使用字典推导式的方法进行反转:

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in my_dict.items()}

第三种解决方案。使用还原倒置方法(依赖于第二种解决方案):

# Use this code to invert dictionaries that have lists of values

my_dict = {value: key for key in inverted_dict for value in my_map[key]}

9
"dict" 是保留字,不应该用作变量名。 - crypdick
5
忘了告诉我们 my_map 是什么。 - crypdick
dictio()?您是不是想说 dict() - Georgy

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