Python 交换嵌套字典的内外键

Python 交换嵌套字典的内外键,python,dictionary,Python,Dictionary,我有下面的字典 {'a':{'se':3, 'op':2}, 'b':{'se':4, 'op':3}} 我需要将其转换如下: {'se':{'a':3, 'b': 4}, 'op':{'a':2,'b':3}} 这是我能想到的以下代码: from collections import defaultdict a = {'a':{'se':3, 'op':2}, 'b':{'se':4, 'op':3}} b = defaultdict(dict) for key1, value1 in

我有下面的字典

{'a':{'se':3, 'op':2}, 'b':{'se':4, 'op':3}}
我需要将其转换如下:

{'se':{'a':3, 'b': 4}, 'op':{'a':2,'b':3}}
这是我能想到的以下代码:

from collections import defaultdict

a = {'a':{'se':3, 'op':2}, 'b':{'se':4, 'op':3}}
b = defaultdict(dict)
for key1, value1 in a.items():
    for key2, value2 in value1.items():
        b[key2].update({key1: value2})

下面的内容完成了任务,但我喜欢一句俏皮话。是否有一个以上的一行程序或更好的方法(更好的性能,例如消除两个循环)?

将其转换为一行程序有多种方法可以实现,但所有这些方法都会很难看。例如:

# Gather k2-k1-v2
g = ((k2,k1,v2) for k1,v1 in a.items() for k2,v2 in v1.items())
# Sort by k2
sg = sorted(g)
# Group by k2
gsg = itertools.groupby(sg, key=operator.itemgetter(0))
# Turn it into a dict of dicts
b = {k: {ksk[1]: ksk[2] for ksk in group} for (k, group) in gsg}
总而言之:

b = {k: {ksk[1]: ksk[2] for ksk in group} 
     for (k, group) in itertools.groupby(
         sorted(((k2,k1,v2) for k1,v1 in a.items() for k2,v2 in v1.items())),
         key=operator.itemgetter(0))}
这是一个表达式,如果你不知道有多少列,你可以把它放在一行。但它肯定不如你的原始版本可读

至于表现呢?快速测试大约需要两倍的时间。Coldspeed的版本介于两者之间。将一个列表更改为迭代器会使它在小的dict(如原始示例)上稍微慢一点,但在大的dict上要快得多,但无论如何,它在任何测试中都没有超过原始列表,并且比具有非常大值的itertools版本慢。当然,如果性能真的很重要,您应该根据实际数据来衡量它们


如果你仔细想想,就没有任何方法可以消除嵌套循环(除非用类似的东西替换其中一个循环,比如递归,或者根据你的例子恰好在每个内部dict中有两个项的事实展开它,这对于你真正的问题来说可能不是真的)。毕竟,您必须访问每个内部字典的每个键,如果没有外部字典上的循环,这是无法做到的。可以将这些循环转换为理解循环,而不是语句,或者将
map
list
中的C循环(或者可能在某些函数中?)。我的版本和Coldspeed都对嵌套循环进行了理解,并且至少有一个额外的线性循环(这不会增加算法的复杂性,但可能会显著增加像您的示例这样的小集合的实际时间)被嵌入到内置函数中。但是,在做更多总体工作的同时加快循环并不总是一个值得权衡的问题。

因此,这改进了@cs95,并提供了更具可读性的1行。 这里有两行,但其中一行可能已经有了内部键(“k”)。 关键是您可以使用dict“a”来传递值

a = {'a':{'se':3, 'op':2}, 'b':{'se':4, 'op':3}}
k = list(a.values())[0].keys()
b = {i: {o: a[o][i] for o in a} for i in k}  # one line dict inversion
print(f'{a}\n{b}')
然而,如果你这样做,你可能没有使用最好的数据结构;相反,您可以使用元组键控的字典,例如

a = {('a', 'se'):3, ('a', 'op'):2, ('b', 'se'):4, ('b', 'op'):3}
然后可以按元组位置排序,并按元组键过滤

c = sorted(a, key=lambda x:x[1])
d = sorted(a, key=lambda x:x[0])
e = list(filter(lambda x:x[0] == 'a', a))  # list 
print(f'a: {a}\nc: {c}\nd: {d}\ne: {e}')
屈服

a: {('a', 'se'): 3, ('a', 'op'): 2, ('b', 'se'): 4, ('b', 'op'): 3}
c: [('a', 'op'), ('b', 'op'), ('a', 'se'), ('b', 'se')]
d: [('a', 'se'), ('a', 'op'), ('b', 'se'), ('b', 'op')]
e: [('a', 'se'), ('a', 'op')]
当然,您仍然可以使用键访问对象:

x = a['a', 'op']  # returns 2

更好的可能是,如果您使用的是一组固定的键,则使用一个枚举元组而不是str。

我尝试了以下方法:{key2:{key1:value2}用于key2,value1.items()中的value2用于key1,value1用于a.items()}但是当一个行程序不能提供性能且不可读时,它会使用最新的值(不太有用)进行过度写入,写这篇文章没有意义。说得对@abarnert