辞書キーの名前を変更する


401

値を新しい名前に割り当て直して古い名前のキーを削除せずに、辞書のキーの名前を変更する方法はありますか。そして、dictキー/値を反復することなく?

OrderedDictの場合は、そのキーの位置を維持しながら同じことを行います。


7
「その値を新しい名前に再割り当てして古い名前のキーを削除せずに」とはどういう意味ですか?私がそれを見る方法、それはキーの名前を変更することの定義であり、以下のすべての答えは値を再割り当てし、古い​​キー名を削除します。あなたはまだ答えを受け入れていないので、おそらくこれらはあなたが求めていることを達成していないのですか?
dbliss 2015

5
本当にバージョン番号を指定する必要があります。 Python 3.7以降、言語仕様では、dictが挿入順に従うことが保証されています。また、(a)2.xまたは3.6にバックポートするコードが必要な場合(またはb)OrderedDictがPython 3.7で冗長になることに注意していない限り、OrderedDictはほとんど時代遅れになります。)。3.6に戻ると、CPythonの実装では辞書の挿入順序が保証されていました(ただし言語仕様は保証されていませんでした)。
smci

1
@smci Python 3.7では、dictは挿入順に従いますが、とはまだ異なりOrderedDictます。dictは、等しいかどうかを比較するときに順序を無視しますが、比較するときにOrderedDict順序を考慮します。あなたがそれを説明する何かにリンクしたのは知っていますが、あなたのコメントがそのリンクを読んでいない人を誤解させたのではないかと思いました。
Flimm

@Flimm:それは本当ですが、私はそれが重要であることを見ることができません、この質問は2つの質問を1つに質問し、2番目の部分の意図は取って代わられました。「ディクショナリは、等しいかどうかを比較するときに順序を無視します」順序で比較することは想定されていないため、はい。OrderedDict比較の際に順序が考慮されるのに対して」 OK、しかし3.7以降はだれも気にしません。私OrderedDictは主に時代遅れであると主張していますが、そうでない理由を具体的に説明できますか?たとえば、必要でない限り、ここでは説得力がありませんreversed()
smci

@Flimm:OrderedDictが3.7以降のコードで3.7または2.xより前のバージョンと互換性がある必要がない限り廃止されていない理由について、信頼できる引数は見つかりません。したがって、たとえばこれはまったく説得力がありません。特に、「OrderedDictを使用すると意図が伝わる...」というのは、完全に必要な技術的負債と難読化に対する恐ろしい議論です。人々は単純にディクテーションに切り替えて、それに取り掛かるべきです。とても簡単です。
smci

回答:


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で機能すると思います。
ダニエル

5
いいえ、OrderedDict挿入順序は保持されますが、これは質問の対象ではありません。
2017

64

1行での最良の方法:

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

3
持っているとしd = {('test', 'foo'):[0,1,2]}たら?
Petr Fedosov 2016年

4
@PetrFedosov、それからあなたはそうしますd[('new', 'key')] = d.pop(('test', 'foo'))
Robert Siemer

17
この回答は、まったく同じことを示唆するwimの回答から2年後、追加情報はありませんでした。何が欠けていますか?
Andras Deak

@AndrasDeak dict()とOrderedDict()の
違い

4
2013年からの最初の改訂におけるwimの回答は、それ以降追加されただけです。秩序は、OPの基準からのみ生じます。
Andras Deak

29

のチェックを使用するとnewkey!=oldkey、この方法で次のことができます。

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

4
これは美しく機能しますが、新しいキーがデフォルトで最後に追加されるため(python3の場合)、元の順序は維持されません。
szeitlin 2018年

2
@szeitlin dict順序に依存すべきではありません。たとえpython3.6 +が順序付けられた形式で初期化しても、以前のバージョンはそうではなく、実際にはpy3.6 + dictがどのように変更されたかの影響だけの機能ではありません。ごOrderedDict注文を気にされる場合にご利用ください。
グラニトサウルス

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 dictは順序付けされて
おら


10

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

1
@MERoseモジュール検索パスのどこかにPythonファイルを追加し、OrderedDictそこからインポートします。しかし、私はお勧めします:継承するクラスを作成しOrderedDict、それにrenameメソッドを追加します。
Ashwini Chaudhary 2015年

OrderedDictが二重にリンクされたリストを使用するように書き直されたようですので、おそらくこれを行う方法はまだありますが、より多くのアクロバットが必要です。:-/
szeitlin 2018年

6

私より前の数人の人.popが、ワンライナーでキーを削除して作成するトリックについて述べました。

私は個人的に、より明示的な実装の方が読みやすいと感じています。

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

上記のコードは戻ります {'a': 1, 'c': 2}


1

他の答えはかなり良いですが、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

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 notebookは選択した順に辞書を表示することに注意してください...


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...

変化するdictへの参照を解放するには、my_dict.keys()をリストに変換する必要がありました。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私はあなたの機能が好きです。以下は、1回の呼び出しで複数のキーの名前を変更するための変更です。

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 callabletemp_dict('k3')ている場合にIfを返しk3ます。括弧ではなく角括弧を使用する必要があります。ただし、これは新しいキーをディクショナリに追加するだけで、要求に応じて既存のキーの名前を変更しません。
ジャスミン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.