Benennen Sie einen Wörterbuchschlüssel um

401

Gibt es eine Möglichkeit, einen Wörterbuchschlüssel umzubenennen, ohne seinen Wert einem neuen Namen zuzuweisen und den alten Namensschlüssel zu entfernen? und ohne durch diktschlüssel / wert zu iterieren?

Im Fall von OrderedDict machen Sie dasselbe, während Sie die Position dieses Schlüssels beibehalten.

rabin utam
quelle
7
Was genau meinen Sie mit "ohne den Wert einem neuen Namen zuzuweisen und den alten Namensschlüssel zu entfernen"? So wie ich es sehe, ist das die Definition des Umbenennens eines Schlüssels, und alle folgenden Antworten weisen den Wert neu zu und entfernen den alten Schlüsselnamen. Sie haben noch keine Antwort akzeptiert. Vielleicht haben diese nicht das erreicht, was Sie suchen?
dbliss
5
Sie müssen wirklich die Versionsnummer (n) angeben. Ab Python 3.7 garantiert die Sprachspezifikation jetzt, dass Diktate der Einfügereihenfolge folgen . Dadurch ist OrderedDict auch größtenteils veraltet (es sei denn, a) Sie möchten Code, der auch auf 2.x oder 3.6 zurückportiert, oder b) Sie interessieren sich für die in Will OrderedDict aufgeführten Probleme, die in Python 3.7 überflüssig werden? ). In Version 3.6 wurde die Reihenfolge des Einfügens des Wörterbuchs durch die CPython-Implementierung (jedoch nicht durch die Sprachspezifikation) garantiert.
smci
1
@smci In Python 3.7 folgen Diktate der Einfügereihenfolge, es gibt jedoch immer noch Unterschiede zu OrderedDict. Diktate ignorieren die Reihenfolge, wenn sie auf Gleichheit verglichen werden, während OrderedDictdie Reihenfolge beim Vergleich berücksichtigt wird. Ich weiß, dass Sie mit etwas verlinkt sind, das es erklärt, aber ich dachte, Ihr Kommentar hätte diejenigen irregeführt, die diesen Link möglicherweise nicht gelesen haben.
Flimm
@Flimm: Das stimmt, aber ich kann nicht sehen, dass es wichtig ist. Diese Frage stellt zwei Fragen in einer und die Absicht des zweiten Teils wurde abgelöst. "Diktate ignorieren die Reihenfolge, wenn sie auf Gleichheit verglichen werden" Ja, weil sie nicht nach Reihenfolge vergleichen sollen. "wohingegen OrderedDictbeim Vergleich die Ordnung berücksichtigt wird" Ok, aber niemand kümmert sich nach 3.7. Ich behaupte, es OrderedDictist weitgehend veraltet. Können Sie Gründe nennen, warum dies nicht der Fall ist? zB hier ist nicht überzeugend, es sei denn, Sie brauchenreversed()
smci
@Flimm: Ich kann keine glaubwürdigen Argumente finden, warum OrderedDict für Code in 3.7+ nicht veraltet ist, es sei denn, es muss mit Pre-3.7 oder 2.x kompatibel sein. So zB das ist alles überzeugend nicht. Insbesondere "die Verwendung eines OrderedDict kommuniziert Ihre Absicht ..." ist ein entsetzliches Argument für die absolut notwendige technische Verschuldung und Verschleierung. Die Leute sollten einfach zurück zum Diktieren wechseln und weitermachen. So einfach.
smci

Antworten:

712

Für ein reguläres Diktat können Sie Folgendes verwenden:

mydict[new_key] = mydict.pop(old_key)

Ich denke, für ein OrderedDict müssen Sie ein völlig neues mit einem Verständnis erstellen.

>>> 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')])

Das Ändern des Schlüssels selbst, wie diese Frage zu stellen scheint, ist unpraktisch, da Diktierschlüssel normalerweise unveränderliche Objekte wie Zahlen, Zeichenfolgen oder Tupel sind. Anstatt zu versuchen, den Schlüssel zu ändern, können Sie den "Umbenennen" in Python erreichen, indem Sie den Wert einem neuen Schlüssel zuweisen und den alten Schlüssel entfernen.

wim
quelle
Für Python 3.5 denke ich, dass dict.popes für ein OrderedDict basierend auf meinem Test funktioniert.
Daniel
5
Nein, OrderedDictdie Einfügereihenfolge bleibt erhalten, worüber die Frage nicht gestellt wurde.
wim
64

beste Methode in 1 Zeile:

>>> d = {'test':[0,1,2]}
>>> d['test2'] = d.pop('test')
>>> d
{'test2': [0, 1, 2]}
Tcll
quelle
3
Was ist, wenn Sie haben d = {('test', 'foo'):[0,1,2]}?
Petr Fedosov
4
@PetrFedosov, dann tun Sied[('new', 'key')] = d.pop(('test', 'foo'))
Robert Siemer
17
Diese Antwort kam zwei Jahre nach der Antwort von wim, die genau dasselbe nahe legt, ohne zusätzliche Informationen. Was vermisse ich?
Andras Deak
@AndrasDeak den Unterschied zwischen einem dict () und einem OrderedDict ()
Tcll
4
Die Antwort von wim in seiner ersten Überarbeitung von 2013 gab es seitdem nur noch Ergänzungen. Die Ordnung ergibt sich nur aus dem OP-Kriterium.
Andras Deak
29

Mit einem Check für newkey!=oldkeykönnen Sie auf folgende Weise Folgendes tun:

if newkey!=oldkey:  
    dictionary[newkey] = dictionary[oldkey]
    del dictionary[oldkey]
Srinivas Reddy Thatiparthy
quelle
4
Dies funktioniert wunderbar, behält jedoch nicht die ursprüngliche Reihenfolge bei, da der neue Schlüssel standardmäßig am Ende hinzugefügt wird (in Python3).
szeitlin
2
@szeitlin Sie sollten sich nicht auf die dictReihenfolge verlassen, auch wenn python3.6 + es in geordneter Form initialisiert, frühere Versionen nicht und es ist nicht wirklich eine Funktion, die nur einen Einfluss darauf hat, wie sich py3.6 + -Diktate geändert haben. Verwenden OrderedDictSie diese Option, wenn Sie sich für die Bestellung interessieren.
Granitosaurus
2
Danke @Granitosaurus, du musstest mir das nicht erklären. Das war nicht der Punkt meines Kommentars.
szeitlin
5
@szeitlin Ihr Kommentar implizierte, dass die Tatsache, dass es die Diktatreihenfolge ändert, in irgendeiner Weise, Form oder Form von Bedeutung ist, wenn sich in Wirklichkeit niemand auf die Wörterbuchreihenfolge verlassen sollte, so dass diese Tatsache völlig irrelevant ist
Granitosaurus
11

Bei Umbenennung aller Wörterbuchschlüssel:

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)
Ikbel Benabdessamad
quelle
1
Ist target_dict.keys()garantiert die gleiche Reihenfolge wie in der Definition? Ich dachte, ein Python-Diktat ist ungeordnet und die Reihenfolge aus der Schlüsselansicht eines
Diktats
Ich denke, Sie sollten Ihre Schlüssel irgendwann sortieren ...
Espoir Murhabazi
10

Sie können dieses OrderedDict recipevon Raymond Hettinger geschriebene verwenden und es ändern, um eine renameMethode hinzuzufügen , aber dies wird ein O (N) in der Komplexität sein:

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

Beispiel:

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

Ausgabe:

OrderedDict({'a': 1, 'b': 2, 'c': 3})
foo 1
bar 2
c 3
spam 5
Ashwini Chaudhary
quelle
1
@MERose Fügen Sie die Python-Datei irgendwo in Ihren Modul-Suchpfad ein und importieren Sie OrderedDictsie. Aber ich würde empfehlen: Erstellen Sie eine Klasse, die von erbt, OrderedDictund fügen Sie eine renameMethode hinzu.
Ashwini Chaudhary
Es scheint, als ob OrderedDict inzwischen neu geschrieben wurde, um eine doppelt verknüpfte Liste zu verwenden. Es gibt also wahrscheinlich noch eine Möglichkeit, dies zu tun, aber es erfordert viel mehr Akrobatik. : - /
szeitlin
6

Einige Leute vor mir erwähnten den .popTrick, einen Schlüssel in einem Einzeiler zu löschen und zu erstellen.

Ich persönlich finde die explizitere Implementierung lesbarer:

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

Der obige Code wird zurückgegeben {'a': 1, 'c': 2}

Uri Goren
quelle
1

Andere Antworten sind ziemlich gut. Aber in Python3.6 hat reguläres Diktat auch Ordnung. Daher ist es im Normalfall schwierig, die Position des Schlüssels beizubehalten.

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
helloswift123
quelle
1

In Python 3.6 (ab?) Würde ich mich für den folgenden Einzeiler entscheiden

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()))

was produziert

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

Es kann erwähnenswert sein, dass printdie ipython console / jupyter notebook das Wörterbuch ohne die Anweisung in einer Reihenfolge ihrer Wahl präsentiert ...

KeithWM
quelle
0

Ich habe mir diese Funktion ausgedacht, die das ursprüngliche Wörterbuch nicht mutiert. Diese Funktion unterstützt auch die Liste der Wörterbücher.

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))
Lokesh Sanapalli
quelle
-1

Ich verwende die obige Antwort von @wim mit dict.pop (), wenn ich Schlüssel umbenenne, aber ich habe ein Gotcha gefunden. Das Durchlaufen des Diktats zum Ändern der Schlüssel, ohne die Liste der alten Schlüssel vollständig von der Diktatinstanz zu trennen, führte dazu, dass neue, geänderte Schlüssel in die Schleife verschoben wurden und einige vorhandene Schlüssel fehlten.

Zunächst habe ich es so gemacht:

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

Ich stellte fest, dass das Wörterbuch beim Durchlaufen des Diktats auf diese Weise immer wieder Schlüssel fand, auch wenn es nicht sollte, dh die neuen Schlüssel, die ich geändert hatte! Ich musste die Instanzen vollständig voneinander trennen, um (a) zu vermeiden, dass meine eigenen geänderten Schlüssel in der for-Schleife gefunden wurden, und (b) einige Schlüssel zu finden, die aus irgendeinem Grund nicht in der Schleife gefunden wurden.

Ich mache das jetzt:

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

Das Konvertieren von my_dict.keys () in eine Liste war erforderlich, um den Verweis auf das sich ändernde Diktat zu entfernen. Allein die Verwendung von my_dict.keys () hat mich mit den seltsamen Nebenwirkungen an die ursprüngliche Instanz gebunden.

Excyberlabber
quelle
-1

Falls jemand alle Schlüssel auf einmal umbenennen möchte und eine Liste mit den neuen Namen bereitstellt:

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()}
Sirmione
quelle
-1

@ helloswift123 Ich mag deine Funktion. Hier ist eine Änderung zum Umbenennen mehrerer Schlüssel in einem einzigen Aufruf:

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
Schädelbecher1089
quelle
-2

Angenommen, Sie möchten den Schlüssel k3 in k4 umbenennen:

temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')
Shishir
quelle
1
Funktioniert nicht, meintest du ... temp_dict ['k4'] = temp_dict.pop ('k3')?
DougR
Dies gibt ein TypeError: 'dict' object is not callableWenn temp_dict('k3')Sie versuchen, auf den Wert des k3Schlüssels zu verweisen , sollten Sie eckige Klammern und keine Klammern verwenden. Dadurch wird jedoch nur ein neuer Schlüssel zum Wörterbuch hinzugefügt und ein vorhandener Schlüssel nicht wie gewünscht umbenannt.
Jasmine