Переименовать ключ словаря

Есть ли способ переименовать ключ словаря без переназначения его значения новому имени и удаления старого ключа имени; и без итерации через ключ / значение dict?

В случае OrderedDict сделайте то же самое, сохранив позицию ключа.


person rabin utam    schedule 10.05.2013    source источник
comment
что именно вы имеете в виду, говоря без переназначения его значения новому имени и удаления старого ключа имени? как я это вижу, это определение переименования ключа, и все ответы ниже переназначают значение и удаляют старое имя ключа. вы еще не приняли ответа, так что, возможно, они не достигли того, что вы ищете?   -  person dbliss    schedule 06.04.2015
comment
@smci В Python 3.7 dicts следуют порядку вставки, однако они по-прежнему отличаются от OrderedDict. dicts игнорирует порядок при сравнении их на равенство, тогда как OrderedDict учитывает порядок при сравнении. Я знаю, что вы связались с чем-то, что объясняет это, но я подумал, что ваш комментарий мог ввести в заблуждение тех, кто, возможно, не читал эту ссылку.   -  person Flimm    schedule 05.12.2019
comment
@Flimm: это правда, но я не понимаю, что это имеет значение, этот вопрос задает два вопроса в одном, и цель второй части была заменена. dicts игнорируют порядок при сравнении их на равенство Да, потому что они не должны сравниваться по порядку. тогда как OrderedDict учитывает порядок при сравнении Хорошо, но после версии 3.7 никому нет дела. Я утверждаю, что OrderedDict в значительной степени устарело, можете ли вы сформулировать причины, по которым это не так? например здесь неубедителен, если вам не нужен reversed()   -  person smci    schedule 06.12.2019
comment
@Flimm: я не могу найти никаких убедительных аргументов, почему OrderedDict не устарел в коде в 3.7+, если только он не должен быть совместим с pre-3.7 или 2.x. Так, например, это неубедительно. В частности, использование OrderedDict сообщает о вашем намерении ... - ужасный аргумент в пользу полностью необходимой технической задолженности и запутывания. Люди должны просто вернуться к диктовке и продолжить. Так просто.   -  person smci    schedule 06.12.2019
comment
@smci Я не согласен с вашим мнением о том, что различия между dict и OrderedDict можно скрыть, и что OrderedDict теперь устарело. Я думаю, что обсуждение этого мнения не в теме для этого вопроса. Вопрос, на который вы указали, - лучшее место, чтобы об этом поговорить. Интересно, что единственный одобренный ответ на вопрос, который вы связали, согласен с тем, что различия имеют значение и что OrderedDict не является устаревшим. Если у вас есть другой ответ, отправьте его туда и позвольте сообществу проголосовать. stackoverflow.com/q/50872498/247696   -  person Flimm    schedule 06.12.2019
comment
Дубликат stackoverflow.com/q/4406501/2506522?   -  person betontalpfa    schedule 29.08.2020
comment
Отвечает ли это на ваш вопрос? Изменить имя ключа в словаре   -  person betontalpfa    schedule 29.08.2020


Ответы (13)


Для обычного дикта вы можете использовать:

mydict[k_new] = mydict.pop(k_old)

Это переместит элемент в конец словаря, если k_new уже не существует, и в этом случае он перезапишет значение на месте.

Для dict Python 3.7+, в котором вы дополнительно хотите сохранить порядок, проще всего перестроить полностью новый экземпляр. Например, переименование ключа 2 в 'two':

>>> d = {0:0, 1:1, 2:2, 3:3}
>>> {"two" if k == 2 else k:v for k,v in d.items()}
{0: 0, 1: 1, 'two': 2, 3: 3}

То же самое верно и для OrderedDict, где вы не можете использовать синтаксис понимания dict, но вы можете использовать выражение генератора:

OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())

Изменение самого ключа, как требует вопрос, нецелесообразно, поскольку ключи hashable, что обычно подразумевает, что они неизменяемы и не может быть изменен.

person wim    schedule 10.05.2013
comment
Для python 3.5 я думаю, что dict.pop работает для OrderedDict на основе моего теста. - person Daniel; 05.12.2017
comment
Нет, OrderedDict сохранит порядок вставки, о котором не спрашивается. - person wim; 05.12.2017
comment
При использовании подхода с перестройкой полностью нового экземпляра (хотя нужно изменить только один ключ), какова будет производительность для действительно огромных словарей (много элементов)? - person Nerxis; 15.06.2021

Используя чек на newkey!=oldkey, вы можете:

if newkey!=oldkey:  
    dictionary[newkey] = dictionary[oldkey]
    del dictionary[oldkey]
person Srinivas Reddy Thatiparthy    schedule 10.05.2013
comment
это прекрасно работает, но не поддерживает исходный порядок, потому что новый ключ добавляется в конце по умолчанию (в python3). - person szeitlin; 02.10.2018
comment
@szeitlin, вы не должны полагаться на порядок dict, даже если python3.6 + инициализирует его в упорядоченном виде, в предыдущих версиях этого не происходит, и на самом деле это не функция, а просто следствие того, как были изменены py3.6 + dicts. Используйте OrderedDict, если вам важен порядок. - person Granitosaurus; 15.11.2018
comment
Спасибо, @Granitosaurus, мне не нужно было объяснять мне это. Мой комментарий был не в этом. - person szeitlin; 15.11.2018
comment
@szeitlin ваш комментарий подразумевает, что тот факт, что он меняет порядок слов, каким-то образом имеет значение, форму или форму, когда на самом деле никто не должен полагаться на порядок словаря, поэтому этот факт совершенно не имеет значения - person Granitosaurus; 16.11.2018

В случае переименования всех ключей словаря:

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)
person Ikbel benab    schedule 17.06.2019
comment
Гарантируется ли, что target_dict.keys() будет в том же порядке, что и в определении? Я думал, что Python dict неупорядочен, а порядок из представления ключей dict непредсказуем - person ttimasdf; 14.01.2020
comment
Это изменилось после Python 3.6 см. docs.python.org/3/library / - person Sebastian; 17.02.2021

Вы можете использовать этот OrderedDict recipe, написанный Раймондом Хеттингером, и изменить его, добавив метод 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
person Ashwini Chaudhary    schedule 10.05.2013
comment
@MERose Добавьте файл Python где-нибудь в пути поиска вашего модуля и импортируйте из него OrderedDict. Но я бы порекомендовал: создать класс, наследующий от OrderedDict, и добавить к нему метод rename. - person Ashwini Chaudhary; 09.06.2015
comment
Похоже, что OrderedDict с тех пор был переписан для использования двусвязного списка, так что, вероятно, еще есть способ сделать это, но для этого требуется гораздо больше акробатики. : - / - person szeitlin; 02.10.2018

Несколько человек до меня упомянули трюк .pop для удаления и создания ключа в однострочном формате.

Я лично считаю, что более явная реализация более читабельна:

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

Приведенный выше код возвращает {'a': 1, 'c': 2}

person Uri Goren    schedule 07.03.2018

Другие ответы довольно хороши, но в python3.6 обычный dict тоже имеет порядок. Так что в обычном случае сложно сохранить положение ключа.

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
person helloswift123    schedule 24.08.2018

В 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 представляет словарь в порядке их выбора ...

person KeithWM    schedule 07.05.2019

Предположим, вы хотите переименовать ключ k3 в k4:

temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')
person Shishir    schedule 29.05.2019
comment
Не работает, вы имели в виду ... temp_dict ['k4'] = temp_dict.pop ('k3')? - person DougR; 04.09.2019
comment
Это вернет TypeError: 'dict' object is not callable. Если с temp_dict('k3') вы пытаетесь ссылаться на значение ключа k3, вам следует использовать квадратные скобки, а не скобки. Однако это просто добавит новый ключ в словарь и не будет переименовывать существующий ключ, как было запрошено. - person Mewtwo; 11.11.2019

Я использую ответ @wim выше с dict.pop () при переименовании ключей, но я обнаружил ошибку. Циклическое прохождение dict для смены ключей без полного отделения списка старых ключей от экземпляра dict привело к циклическому включению новых, измененных ключей в цикл и отсутствию некоторых существующих ключей.

Для начала я сделал так:

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

Я обнаружил, что, циклически перебирая dict таким образом, словарь продолжал находить ключи, даже когда этого не должно было быть, то есть новые ключи, те, которые я изменил! Мне нужно было полностью отделить экземпляры друг от друга, чтобы (а) избежать нахождения моих собственных измененных ключей в цикле for и (б) найти некоторые ключи, которые по какой-то причине не были найдены в цикле.

Я делаю это сейчас:

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

Преобразование my_dict.keys () в список было необходимо, чтобы освободиться от ссылки на изменяющийся dict. Простое использование my_dict.keys () удерживало меня привязанным к исходному экземпляру со странными побочными эффектами.

person excyberlabber    schedule 21.03.2019

Если кто-то захочет переименовать сразу все ключи, предоставив список с новыми именами:

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()}
person Sirmione    schedule 07.05.2019

@ 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
person skullgoblet1089    schedule 22.08.2019

Я придумал эту функцию, которая не изменяет исходный словарь. Эта функция также поддерживает список словарей.

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))
person Lokesh Sanapalli    schedule 28.05.2020

Я объединил несколько ответов из вышеупомянутой ветки и нашел решение ниже. Хотя это просто, его можно использовать как строительный блок для выполнения более сложных обновлений ключей из словаря.

test_dict = {'a': 1, 'b': 2, 'c': 3}
print(test_dict)
# {'a': 1, 'b': 2, 'c': 3}
prefix = 'up'
def dict_key_update(json_file):    
    new_keys = []
    old_keys = []
    for i,(key,value) in enumerate(json_file.items()):
        old_keys.append(key)
        new_keys.append(str(prefix) + key) # i have updated by adding a prefix to the 
        # key
    for old_key, new_key in zip(old_keys,new_keys):
        print('old {}, new {}'.format(old_key, new_key))
        if new_key!=old_key:  
           json_file[new_key] = json_file.pop(old_key)
     return json_file

test_dict = dict_key_update(test_dict)
print(test_dict)
# {'upa': 1, 'upb': 2, 'upc': 3}
person burhan rashid    schedule 11.01.2021