重命名字典键


400

有没有一种方法可以重命名字典键,而无需将其值重新分配给新名称并删除旧名称键;而不迭代字典键/值?

对于OrderedDict,在保持键的位置的同时执行相同的操作。


7
“不将其值重新分配给新名称并删除旧名称键”到底是什么意思?我的看法是重命名键的定义,下面的所有答案都重新分配了值并删除了旧的键名。您尚未接受答案,那么也许这些还没有完成您想要的?
dbliss 2015年

5
您确实需要指定版本号。 从Python 3.7开始,语言规范现在保证字典遵循插入顺序。这也使得OrderedDict几乎已经过时了(除非a)您希望代码也可以反向移植到2.x或3.6-或b)您关心的是OrderedDict中列出的问题在Python 3.7中是否会变得多余?)。在3.6版本中,字典插入顺序由CPython实现(但不是语言规范)保证。
smci

1
@smci在Python 3.7中,dicts遵循插入顺序,但是与仍然不同OrderedDict。字典在进行相等性比较时会忽略顺序,而OrderedDict在进行比较时会考虑顺序。我知道您链接到了可以解释它的内容,但我认为您的评论可能误导了那些可能未阅读该链接的人。
Flimm

@Flimm:是的,但是我看不到这很重要,这个问题合二为一,第二部分的意图已被取代。“字典在进行相等性比较时会忽略顺序”是的,因为不应按顺序进行比较。OrderedDict比较时考虑顺序”,但是没有人关心3.7之后的情况。我声称OrderedDict它已经过时了,您能说清楚为什么它不是吗?例如除非您需要,否则此处没有说服力reversed()
smci

@Flimm:我找不到任何可靠的论据,除非OrderedDict必须与3.7或2.x之前版本兼容,否则在3.7+中的代码上OrderedDict不会过时。因此,例如,根本没有说服力。特别是“使用OrderedDict传达您的意图...”是完全必要的技术债务和混淆的令人震惊的论点。人们应该简单地切换回字典并继续使用它。这么简单。
smci

Answers:


712

对于常规命令,可以使用:

mydict[new_key] = mydict.pop(old_key)

对于OrderedDict,我认为您必须使用一种理解来构建一个全新的。

>>> OrderedDict(zip('123', 'abc'))
OrderedDict([('1', 'a'), ('2', 'b'), ('3', 'c')])
>>> oldkey, newkey = '2', 'potato'
>>> OrderedDict((newkey if k == oldkey else k, v) for k, v in _.viewitems())
OrderedDict([('1', 'a'), ('potato', 'b'), ('3', 'c')])

正如这个问题似乎提出的那样,修改密钥本身是不切实际的,因为dict密钥通常是不可变的对象,例如数字,字符串或元组。您可以尝试在python中实现“重命名”,而不是尝试修改键,而是将值重新分配给新键并删除旧键。


对于python 3.5,dict.pop根据我的测试,我认为适用于OrderedDict。
丹尼尔(Daniel)

5
不,OrderedDict将保留插入顺序,这不是问题所要解决的。
wim

64

1行中的最佳方法:

>>> d = {'test':[0,1,2]}
>>> d['test2'] = d.pop('test')
>>> d
{'test2': [0, 1, 2]}

3
如果有的话怎么d = {('test', 'foo'):[0,1,2]}办?
彼得·费多索夫

4
@PetrFedosov,然后您去做d[('new', 'key')] = d.pop(('test', 'foo'))
罗伯特·

17
这个答案是在wim的答案提出两年后提出的,它暗示了完全相同的事情,没有其他信息。我想念什么?
安德拉斯·迪克

@AndrasDeak dict()和OrderedDict()之间的区别
Tcll

4
wim在2013年首次修订时给出了答案,此后仅进行了补充。有序性仅来自OP的标准。
安德拉斯·迪克

29

通过使用check newkey!=oldkey,可以这样:

if newkey!=oldkey:  
    dictionary[newkey] = dictionary[oldkey]
    del dictionary[oldkey]

4
这可以很好地工作,但不会保持原始顺序,因为默认情况下,新密钥会在末尾添加(在python3中)。
szeitlin '18 -10-1

2
@szeitlin您不应该依赖dict顺序,即使python3.6 +以有序形式将其初始化,以前的版本也没有,并且它并不是一个真正的功能,只是py3.6 +字典更改方式的后遗症。OrderedDict如果您关心订单,请使用。
Granitosaurus

2
感谢@Granitosaurus,我不需要您向我解释。那不是我评论的重点。
szeitlin

5
@szeitlin您的评论暗示,它改变辞令顺序的事实很重要,实际上,任何人都不应该依赖字典顺序,因此这一事实是完全不相关的
Granitosaurus

11

如果重命名所有字典键:

target_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
new_keys = ['k4','k5','k6']

for key,n_key in zip(target_dict.keys(), new_keys):
    target_dict[n_key] = target_dict.pop(key)

1
是否target_dict.keys()保证与定义中的顺序相同?我认为Python字典是无序的,并且字典键视图的顺序是无法预测的
ttimasdf

我认为您应该在某些时候对密钥进行排序...
Espoir Murhabazi

10

您可以使用OrderedDict recipeRaymond Hettinger编写的代码并对其进行修改以添加一个rename方法,但这将成为O(N)的复杂性:

def rename(self,key,new_key):
    ind = self._keys.index(key)  #get the index of old key, O(N) operation
    self._keys[ind] = new_key    #replace old key with new key in self._keys
    self[new_key] = self[key]    #add the new key, this is added at the end of self._keys
    self._keys.pop(-1)           #pop the last item in self._keys

例:

dic = OrderedDict((("a",1),("b",2),("c",3)))
print dic
dic.rename("a","foo")
dic.rename("b","bar")
dic["d"] = 5
dic.rename("d","spam")
for k,v in  dic.items():
    print k,v

输出:

OrderedDict({'a': 1, 'b': 2, 'c': 3})
foo 1
bar 2
c 3
spam 5

1
@MERose将Python文件添加到模块搜索路径中的某个位置,然后OrderedDict从中导入。但我建议:创建一个继承自该类的类,OrderedDictrename为其添加一个方法。
Ashwini Chaudhary

此后似乎已经将OrderedDict重写为使用双向链接列表,因此可能仍然可以这样做,但是需要更多的杂技。:-/
szeitlin '18年

6

在我之前的一些人提到了.pop一种删除和创建单行密钥的技巧。

我个人认为更明确的实现更具可读性:

d = {'a': 1, 'b': 2}
v = d['b']
del d['b']
d['c'] = v

上面的代码返回 {'a': 1, 'c': 2}


1

其他答案也不错。但是在python3.6中,常规字典也有顺序。因此在正常情况下很难保持钥匙的位置。

def rename(old_dict,old_name,new_name):
    new_dict = {}
    for key,value in zip(old_dict.keys(),old_dict.values()):
        new_key = key if key != old_name else new_name
        new_dict[new_key] = old_dict[key]
    return new_dict

1

在Python 3.6中(继续吗?),我将采用以下一种形式

test = {'a': 1, 'old': 2, 'c': 3}
old_k = 'old'
new_k = 'new'
new_v = 4  # optional

print(dict((new_k, new_v) if k == old_k else (k, v) for k, v in test.items()))

产生

{'a': 1, 'new': 4, 'c': 3}

可能值得注意的是,如果没有print声明,ipython console / jupyter笔记本将按其选择的顺序显示字典。


0

我想出了这个功能,它不会使原始字典发生变异。该功能也支持字典列表。

import functools
from typing import Union, Dict, List


def rename_dict_keys(
    data: Union[Dict, List[Dict]], old_key: str, new_key: str
):
    """
    This function renames dictionary keys

    :param data:
    :param old_key:
    :param new_key:
    :return: Union[Dict, List[Dict]]
    """
    if isinstance(data, dict):
        res = {k: v for k, v in data.items() if k != old_key}
        try:
            res[new_key] = data[old_key]
        except KeyError:
            raise KeyError(
                "cannot rename key as old key '%s' is not present in data"
                % old_key
            )
        return res
    elif isinstance(data, list):
        return list(
            map(
                functools.partial(
                    rename_dict_keys, old_key=old_key, new_key=new_key
                ),
                data,
            )
        )
    raise ValueError("expected type List[Dict] or Dict got '%s' for data" % type(data))

-1

重命名密钥时,我在dict.pop()中使用了@wim的答案,但是我发现了一个问题。在dict上循环以更改键,而又未将旧键列表与dict实例完全分开,导致将新的,已更改的键循环到循环中,并丢失了一些现有键。

首先,我这样做:

for current_key in my_dict:
    new_key = current_key.replace(':','_')
    fixed_metadata[new_key] = fixed_metadata.pop(current_key)

我发现字典以这种方式循环,即使在不应该的时候,字典也一直在寻找键,例如,新的键,即我已更改的键!我需要将实例彼此完全分开,以(a)避免在for循环中找到自己更改的键,以及(b)由于某些原因而在循环中找不到某些键。

我现在正在这样做:

current_keys = list(my_dict.keys())
for current_key in current_keys:
    and so on...

将my_dict.keys()转换为列表对于释放对更改的dict的引用是必要的。仅使用my_dict.keys()就使我与原始实例保持联系,并产生了奇怪的副作用。


-1

如果有人想一次重命名所有键,并提供一个包含新名称的列表:

def rename_keys(dict_, new_keys):
    """
     new_keys: type List(), must match length of dict_
    """

    # dict_ = {oldK: value}
    # d1={oldK:newK,} maps old keys to the new ones:  
    d1 = dict( zip( list(dict_.keys()), new_keys) )

          # d1{oldK} == new_key 
    return {d1[oldK]: value for oldK, value in dict_.items()}

-1

@ helloswift123我喜欢您的功能。这是在单个调用中重命名多个键的修改:

def rename(d, keymap):
    """
    :param d: old dict
    :type d: dict
    :param keymap: [{:keys from-keys :values to-keys} keymap]
    :returns: new dict
    :rtype: dict
    """
    new_dict = {}
    for key, value in zip(d.keys(), d.values()):
        new_key = keymap.get(key, key)
        new_dict[new_key] = d[key]
    return new_dict

-2

假设您要将键k3重命名为k4:

temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')

1
不起作用,您是说... temp_dict ['k4'] = temp_dict.pop('k3')吗?
DougR

TypeError: 'dict' object is not callable如果temp_dict('k3')尝试引用该k3键的值,则将返回If,则应使用方括号而不是括号。但是,这只会在字典中添加一个新键,而不会按要求重命名现有键。
茉莉花
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.