Есть ли способ переименовать ключ словаря без переназначения его значения новому имени и удаления старого ключа имени; и без итерации через ключ / значение dict?
В случае OrderedDict сделайте то же самое, сохранив позицию ключа.
Переименовать ключ словаря
Ответы (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, что обычно подразумевает, что они неизменяемы и не может быть изменен.
dict.pop
работает для OrderedDict на основе моего теста.
- person Daniel; 05.12.2017
OrderedDict
сохранит порядок вставки, о котором не спрашивается.
- person wim; 05.12.2017
Используя чек на newkey!=oldkey
, вы можете:
if newkey!=oldkey:
dictionary[newkey] = dictionary[oldkey]
del dictionary[oldkey]
dict
, даже если python3.6 + инициализирует его в упорядоченном виде, в предыдущих версиях этого не происходит, и на самом деле это не функция, а просто следствие того, как были изменены py3.6 + dicts. Используйте OrderedDict
, если вам важен порядок.
- person Granitosaurus; 15.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)
target_dict.keys()
будет в том же порядке, что и в определении? Я думал, что Python dict неупорядочен, а порядок из представления ключей dict непредсказуем
- person ttimasdf; 14.01.2020
Вы можете использовать этот 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
OrderedDict
. Но я бы порекомендовал: создать класс, наследующий от OrderedDict
, и добавить к нему метод rename
.
- person Ashwini Chaudhary; 09.06.2015
Несколько человек до меня упомянули трюк .pop
для удаления и создания ключа в однострочном формате.
Я лично считаю, что более явная реализация более читабельна:
d = {'a': 1, 'b': 2}
v = d['b']
del d['b']
d['c'] = v
Приведенный выше код возвращает {'a': 1, 'c': 2}
Другие ответы довольно хороши, но в 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
В 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 представляет словарь в порядке их выбора ...
Предположим, вы хотите переименовать ключ k3 в k4:
temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')
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 () удерживало меня привязанным к исходному экземпляру со странными побочными эффектами.
Если кто-то захочет переименовать сразу все ключи, предоставив список с новыми именами:
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()}
@ 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
Я придумал эту функцию, которая не изменяет исходный словарь. Эта функция также поддерживает список словарей.
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))
Я объединил несколько ответов из вышеупомянутой ветки и нашел решение ниже. Хотя это просто, его можно использовать как строительный блок для выполнения более сложных обновлений ключей из словаря.
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}
OrderedDict
. dicts игнорирует порядок при сравнении их на равенство, тогда какOrderedDict
учитывает порядок при сравнении. Я знаю, что вы связались с чем-то, что объясняет это, но я подумал, что ваш комментарий мог ввести в заблуждение тех, кто, возможно, не читал эту ссылку. - person Flimm   schedule 05.12.2019OrderedDict
учитывает порядок при сравнении Хорошо, но после версии 3.7 никому нет дела. Я утверждаю, чтоOrderedDict
в значительной степени устарело, можете ли вы сформулировать причины, по которым это не так? например здесь неубедителен, если вам не нуженreversed()
- person smci   schedule 06.12.2019dict
иOrderedDict
можно скрыть, и чтоOrderedDict
теперь устарело. Я думаю, что обсуждение этого мнения не в теме для этого вопроса. Вопрос, на который вы указали, - лучшее место, чтобы об этом поговорить. Интересно, что единственный одобренный ответ на вопрос, который вы связали, согласен с тем, что различия имеют значение и чтоOrderedDict
не является устаревшим. Если у вас есть другой ответ, отправьте его туда и позвольте сообществу проголосовать. stackoverflow.com/q/50872498/247696 - person Flimm   schedule 06.12.2019